mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 19:57:25 +01:00
Convert dialogs to navigator screens
This commit is contained in:
parent
6a40921ac4
commit
0e7a2f1f3d
56 changed files with 2650 additions and 2888 deletions
|
|
@ -7,7 +7,6 @@
|
||||||
<file preprocess="xml-stripblanks">ui/instrument_editor.ui</file>
|
<file preprocess="xml-stripblanks">ui/instrument_editor.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/instrument_selector.ui</file>
|
<file preprocess="xml-stripblanks">ui/instrument_selector.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/login_dialog.ui</file>
|
<file preprocess="xml-stripblanks">ui/login_dialog.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/part_editor.ui</file>
|
|
||||||
<file preprocess="xml-stripblanks">ui/performance_editor.ui</file>
|
<file preprocess="xml-stripblanks">ui/performance_editor.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/person_editor.ui</file>
|
<file preprocess="xml-stripblanks">ui/person_editor.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/person_list.ui</file>
|
<file preprocess="xml-stripblanks">ui/person_list.ui</file>
|
||||||
|
|
@ -21,13 +20,15 @@
|
||||||
<file preprocess="xml-stripblanks">ui/recording_screen.ui</file>
|
<file preprocess="xml-stripblanks">ui/recording_screen.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/recording_selector.ui</file>
|
<file preprocess="xml-stripblanks">ui/recording_selector.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/recording_selector_screen.ui</file>
|
<file preprocess="xml-stripblanks">ui/recording_selector_screen.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/section_editor.ui</file>
|
<file preprocess="xml-stripblanks">ui/selector.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/server_dialog.ui</file>
|
<file preprocess="xml-stripblanks">ui/server_dialog.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/tracks_editor.ui</file>
|
<file preprocess="xml-stripblanks">ui/tracks_editor.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/track_editor.ui</file>
|
<file preprocess="xml-stripblanks">ui/track_editor.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/window.ui</file>
|
<file preprocess="xml-stripblanks">ui/window.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/work_editor.ui</file>
|
<file preprocess="xml-stripblanks">ui/work_editor.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">ui/work_part_editor.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/work_screen.ui</file>
|
<file preprocess="xml-stripblanks">ui/work_screen.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">ui/work_section_editor.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/work_selector.ui</file>
|
<file preprocess="xml-stripblanks">ui/work_selector.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/work_selector_screen.ui</file>
|
<file preprocess="xml-stripblanks">ui/work_selector_screen.ui</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
|
|
|
||||||
|
|
@ -2,57 +2,84 @@
|
||||||
<!-- Generated with glade 3.38.1 -->
|
<!-- Generated with glade 3.38.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<requires lib="libhandy" version="0.0"/>
|
<requires lib="libhandy" version="1.0"/>
|
||||||
<object class="HdyWindow" id="window">
|
<object class="GtkStack" id="widget">
|
||||||
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="modal">True</property>
|
<property name="transition-type">crossfade</property>
|
||||||
<property name="destroy-with-parent">True</property>
|
|
||||||
<property name="type-hint">dialog</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkStack" id="stack">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="transition-type">crossfade</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="HdyHeaderBar">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="title" translatable="yes">Ensemble</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="HdyHeaderBar">
|
<object class="GtkButton" id="back_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="title" translatable="yes">Ensemble</property>
|
<property name="receives-default">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="cancel_button">
|
<object class="GtkImage">
|
||||||
<property name="label" translatable="yes">Cancel</property>
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="icon-name">go-previous-symbolic</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
</object>
|
||||||
<object class="GtkButton" id="save_button">
|
</child>
|
||||||
<property name="label" translatable="yes">Save</property>
|
<child>
|
||||||
<property name="visible">True</property>
|
<object class="GtkButton" id="save_button">
|
||||||
<property name="can-focus">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="can-focus">True</property>
|
||||||
<style>
|
<property name="receives-default">True</property>
|
||||||
<class name="suggested-action"/>
|
<child>
|
||||||
</style>
|
<object class="GtkImage">
|
||||||
</object>
|
<property name="visible">True</property>
|
||||||
<packing>
|
<property name="can-focus">False</property>
|
||||||
<property name="pack-type">end</property>
|
<property name="icon-name">object-select-symbolic</property>
|
||||||
<property name="position">1</property>
|
</object>
|
||||||
</packing>
|
</child>
|
||||||
</child>
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="pack-type">end</property>
|
||||||
<property name="fill">True</property>
|
<property name="position">1</property>
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkInfoBar" id="info_bar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="revealed">False</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="HdyClamp">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="maximum-size">500</property>
|
||||||
|
<property name="tightening-threshold">300</property>
|
||||||
<child>
|
<child>
|
||||||
<!-- n-columns=2 n-rows=2 -->
|
<!-- n-columns=2 n-rows=2 -->
|
||||||
<object class="GtkGrid">
|
<object class="GtkGrid">
|
||||||
|
|
@ -109,107 +136,54 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkInfoBar" id="info_bar">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="revealed">False</property>
|
|
||||||
<child internal-child="action_area">
|
|
||||||
<object class="GtkButtonBox">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="spacing">6</property>
|
|
||||||
<property name="layout-style">end</property>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child internal-child="content_area">
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="spacing">16</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Failed to save ensemble!</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="name">content</property>
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">content</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="HdyHeaderBar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="title" translatable="yes">Ensemble</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkSpinner">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="vexpand">True</property>
|
||||||
<child>
|
<property name="active">True</property>
|
||||||
<object class="HdyHeaderBar">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="title" translatable="yes">Ensemble</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSpinner">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="vexpand">True</property>
|
|
||||||
<property name="active">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="name">loading</property>
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
<property name="position">1</property>
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">loading</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
|
||||||
|
|
@ -2,57 +2,84 @@
|
||||||
<!-- Generated with glade 3.38.1 -->
|
<!-- Generated with glade 3.38.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<requires lib="libhandy" version="0.0"/>
|
<requires lib="libhandy" version="1.0"/>
|
||||||
<object class="HdyWindow" id="window">
|
<object class="GtkStack" id="widget">
|
||||||
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="modal">True</property>
|
<property name="transition-type">crossfade</property>
|
||||||
<property name="destroy-with-parent">True</property>
|
|
||||||
<property name="type-hint">dialog</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkStack" id="stack">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="transition-type">crossfade</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="HdyHeaderBar">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="title" translatable="yes">Instrument</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="HdyHeaderBar">
|
<object class="GtkButton" id="back_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="title" translatable="yes">Instrument</property>
|
<property name="receives-default">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="cancel_button">
|
<object class="GtkImage">
|
||||||
<property name="label" translatable="yes">Cancel</property>
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="icon-name">go-previous-symbolic</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
</object>
|
||||||
<object class="GtkButton" id="save_button">
|
</child>
|
||||||
<property name="label" translatable="yes">Save</property>
|
<child>
|
||||||
<property name="visible">True</property>
|
<object class="GtkButton" id="save_button">
|
||||||
<property name="can-focus">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="can-focus">True</property>
|
||||||
<style>
|
<property name="receives-default">True</property>
|
||||||
<class name="suggested-action"/>
|
<child>
|
||||||
</style>
|
<object class="GtkImage">
|
||||||
</object>
|
<property name="visible">True</property>
|
||||||
<packing>
|
<property name="can-focus">False</property>
|
||||||
<property name="pack-type">end</property>
|
<property name="icon-name">object-select-symbolic</property>
|
||||||
<property name="position">1</property>
|
</object>
|
||||||
</packing>
|
</child>
|
||||||
</child>
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="pack-type">end</property>
|
||||||
<property name="fill">True</property>
|
<property name="position">1</property>
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkInfoBar" id="info_bar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="revealed">False</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="HdyClamp">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="maximum-size">500</property>
|
||||||
|
<property name="tightening-threshold">300</property>
|
||||||
<child>
|
<child>
|
||||||
<!-- n-columns=2 n-rows=2 -->
|
<!-- n-columns=2 n-rows=2 -->
|
||||||
<object class="GtkGrid">
|
<object class="GtkGrid">
|
||||||
|
|
@ -109,107 +136,54 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkInfoBar" id="info_bar">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="revealed">False</property>
|
|
||||||
<child internal-child="action_area">
|
|
||||||
<object class="GtkButtonBox">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="spacing">6</property>
|
|
||||||
<property name="layout-style">end</property>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child internal-child="content_area">
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="spacing">16</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Failed to save instrument!</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="name">content</property>
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">content</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="HdyHeaderBar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="title" translatable="yes">Instrument</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkSpinner">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="vexpand">True</property>
|
||||||
<child>
|
<property name="active">True</property>
|
||||||
<object class="HdyHeaderBar">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="title" translatable="yes">Instrument</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSpinner">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="vexpand">True</property>
|
|
||||||
<property name="active">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="name">loading</property>
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
<property name="position">1</property>
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">loading</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
|
||||||
|
|
@ -2,53 +2,65 @@
|
||||||
<!-- Generated with glade 3.38.1 -->
|
<!-- Generated with glade 3.38.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<requires lib="libhandy" version="0.0"/>
|
<requires lib="libhandy" version="1.0"/>
|
||||||
<object class="HdyWindow" id="window">
|
<object class="GtkBox" id="widget">
|
||||||
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="modal">True</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="destroy-with-parent">True</property>
|
|
||||||
<property name="type-hint">dialog</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="HdyHeaderBar">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="title" translatable="yes">Performance</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="HdyHeaderBar">
|
<object class="GtkButton" id="back_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="title" translatable="yes">Performance</property>
|
<property name="receives-default">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="cancel_button">
|
<object class="GtkImage">
|
||||||
<property name="label" translatable="yes">Cancel</property>
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="icon-name">go-previous-symbolic</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
</object>
|
||||||
<object class="GtkButton" id="save_button">
|
</child>
|
||||||
<property name="label" translatable="yes">Save</property>
|
<child>
|
||||||
<property name="visible">True</property>
|
<object class="GtkButton" id="save_button">
|
||||||
<property name="sensitive">False</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="sensitive">False</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="can-focus">True</property>
|
||||||
<style>
|
<property name="receives-default">True</property>
|
||||||
<class name="suggested-action"/>
|
<child>
|
||||||
</style>
|
<object class="GtkImage">
|
||||||
</object>
|
<property name="visible">True</property>
|
||||||
<packing>
|
<property name="can-focus">False</property>
|
||||||
<property name="pack-type">end</property>
|
<property name="icon-name">object-select-symbolic</property>
|
||||||
<property name="position">1</property>
|
</object>
|
||||||
</packing>
|
</child>
|
||||||
</child>
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="pack-type">end</property>
|
||||||
<property name="fill">True</property>
|
<property name="position">1</property>
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="HdyClamp">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="maximum-size">500</property>
|
||||||
|
<property name="tightening-threshold">300</property>
|
||||||
<child>
|
<child>
|
||||||
<!-- n-columns=2 n-rows=3 -->
|
<!-- n-columns=2 n-rows=3 -->
|
||||||
<object class="GtkGrid">
|
<object class="GtkGrid">
|
||||||
|
|
@ -249,13 +261,13 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkSizeGroup">
|
<object class="GtkSizeGroup">
|
||||||
|
|
|
||||||
|
|
@ -2,57 +2,84 @@
|
||||||
<!-- Generated with glade 3.38.1 -->
|
<!-- Generated with glade 3.38.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<requires lib="libhandy" version="0.0"/>
|
<requires lib="libhandy" version="1.0"/>
|
||||||
<object class="HdyWindow" id="window">
|
<object class="GtkStack" id="widget">
|
||||||
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="modal">True</property>
|
<property name="transition-type">crossfade</property>
|
||||||
<property name="destroy-with-parent">True</property>
|
|
||||||
<property name="type-hint">dialog</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkStack" id="stack">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="transition-type">crossfade</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="HdyHeaderBar">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="title" translatable="yes">Person</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="HdyHeaderBar">
|
<object class="GtkButton" id="back_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="title" translatable="yes">Person</property>
|
<property name="receives-default">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="cancel_button">
|
<object class="GtkImage">
|
||||||
<property name="label" translatable="yes">Cancel</property>
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="icon-name">go-previous-symbolic</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
</object>
|
||||||
<object class="GtkButton" id="save_button">
|
</child>
|
||||||
<property name="label" translatable="yes">Save</property>
|
<child>
|
||||||
<property name="visible">True</property>
|
<object class="GtkButton" id="save_button">
|
||||||
<property name="can-focus">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="can-focus">True</property>
|
||||||
<style>
|
<property name="receives-default">True</property>
|
||||||
<class name="suggested-action"/>
|
<child>
|
||||||
</style>
|
<object class="GtkImage">
|
||||||
</object>
|
<property name="visible">True</property>
|
||||||
<packing>
|
<property name="can-focus">False</property>
|
||||||
<property name="pack-type">end</property>
|
<property name="icon-name">object-select-symbolic</property>
|
||||||
<property name="position">1</property>
|
</object>
|
||||||
</packing>
|
</child>
|
||||||
</child>
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="pack-type">end</property>
|
||||||
<property name="fill">True</property>
|
<property name="position">1</property>
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkInfoBar" id="info_bar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="revealed">False</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="HdyClamp">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="maximum-size">500</property>
|
||||||
|
<property name="tightening-threshold">300</property>
|
||||||
<child>
|
<child>
|
||||||
<!-- n-columns=2 n-rows=3 -->
|
<!-- n-columns=2 n-rows=3 -->
|
||||||
<object class="GtkGrid">
|
<object class="GtkGrid">
|
||||||
|
|
@ -132,107 +159,54 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkInfoBar" id="info_bar">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="revealed">False</property>
|
|
||||||
<child internal-child="action_area">
|
|
||||||
<object class="GtkButtonBox">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="spacing">6</property>
|
|
||||||
<property name="layout-style">end</property>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child internal-child="content_area">
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="spacing">16</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Failed to save person!</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="name">content</property>
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">content</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="HdyHeaderBar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="title" translatable="yes">Person</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkSpinner">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="vexpand">True</property>
|
||||||
<child>
|
<property name="active">True</property>
|
||||||
<object class="HdyHeaderBar">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="title" translatable="yes">Person</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSpinner">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="vexpand">True</property>
|
|
||||||
<property name="active">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="name">loading</property>
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
<property name="position">1</property>
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">loading</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkCheckButton" id="server_check_button">
|
<object class="GtkCheckButton" id="server_check_button">
|
||||||
<property name="label" translatable="yes">Show works from the server</property>
|
<property name="label" translatable="yes">Show recordings from the server</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="receives-default">False</property>
|
<property name="receives-default">False</property>
|
||||||
|
|
|
||||||
264
musicus/res/ui/selector.ui
Normal file
264
musicus/res/ui/selector.ui
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.38.1 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.24"/>
|
||||||
|
<requires lib="libhandy" version="1.0"/>
|
||||||
|
<object class="GtkBox" id="widget">
|
||||||
|
<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="HdyHeaderBar" id="header">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="back_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">go-previous-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="add_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-add-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</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="HdySearchBar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="search-mode-enabled">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="HdyClamp">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="maximum-size">500</property>
|
||||||
|
<property name="tightening-threshold">300</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSearchEntry" id="search_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="primary-icon-name">edit-find-symbolic</property>
|
||||||
|
<property name="primary-icon-activatable">False</property>
|
||||||
|
<property name="primary-icon-sensitive">False</property>
|
||||||
|
<property name="placeholder-text" translatable="yes">Search …</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="server_check_button">
|
||||||
|
<property name="label" translatable="yes">Use the Musicus server</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="receives-default">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="active">True</property>
|
||||||
|
<property name="draw-indicator">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkStack" id="stack">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="hhomogeneous">False</property>
|
||||||
|
<property name="vhomogeneous">False</property>
|
||||||
|
<property name="transition-type">crossfade</property>
|
||||||
|
<property name="interpolate-size">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSpinner">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="margin-top">12</property>
|
||||||
|
<property name="active">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">loading</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="height-request">200</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="shadow-type">none</property>
|
||||||
|
<child>
|
||||||
|
<object class="HdyClamp">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="maximum-size">500</property>
|
||||||
|
<property name="tightening-threshold">300</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkFrame" id="frame">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="margin-start">6</property>
|
||||||
|
<property name="margin-end">6</property>
|
||||||
|
<property name="margin-top">12</property>
|
||||||
|
<property name="margin-bottom">6</property>
|
||||||
|
<property name="label-xalign">0</property>
|
||||||
|
<property name="shadow-type">in</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child type="label_item">
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">content</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="border-width">18</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">18</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="opacity">0.5</property>
|
||||||
|
<property name="pixel-size">80</property>
|
||||||
|
<property name="icon-name">network-error-symbolic</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="opacity">0.5</property>
|
||||||
|
<property name="label" translatable="yes">An error occured!</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="size" value="16384"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="opacity">0.5</property>
|
||||||
|
<property name="label" translatable="yes">The server was not reachable or responded with an error. Please check your internet connection.</property>
|
||||||
|
<property name="justify">center</property>
|
||||||
|
<property name="wrap">True</property>
|
||||||
|
<property name="max-width-chars">40</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="try_again_button">
|
||||||
|
<property name="label" translatable="yes">Try again</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="receives-default">True</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">error</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
|
|
@ -3,85 +3,117 @@
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.24"/>
|
<requires lib="gtk+" version="3.24"/>
|
||||||
<requires lib="libhandy" version="0.0"/>
|
<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="can-focus">False</property>
|
||||||
<property name="modal">True</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="default-width">350</property>
|
|
||||||
<property name="default-height">200</property>
|
|
||||||
<property name="type-hint">dialog</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="HdyHeaderBar">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="title" translatable="yes">Track</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="HdyHeaderBar">
|
<object class="GtkButton" id="back_button">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="title" translatable="yes">Track</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="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="GtkScrolledWindow">
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
|
<property name="receives-default">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkViewport">
|
<object class="GtkImage">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="shadow-type">none</property>
|
<property name="icon-name">go-previous-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="save_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">object-select-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<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="GtkScrolledWindow">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="shadow-type">none</property>
|
||||||
|
<child>
|
||||||
|
<object class="HdyClamp">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="maximum-size">500</property>
|
||||||
|
<property name="tightening-threshold">300</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkListBox" id="list">
|
<object class="GtkFrame">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="selection-mode">none</property>
|
<property name="valign">start</property>
|
||||||
<child type="placeholder">
|
<property name="margin-start">6</property>
|
||||||
<object class="GtkLabel">
|
<property name="margin-end">6</property>
|
||||||
|
<property name="margin-top">12</property>
|
||||||
|
<property name="margin-bottom">6</property>
|
||||||
|
<property name="label-xalign">0</property>
|
||||||
|
<property name="shadow-type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListBox" id="list">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="label" translatable="yes">Select a recording of a work with multiple parts.</property>
|
<property name="selection-mode">none</property>
|
||||||
|
<child type="placeholder">
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="margin-start">6</property>
|
||||||
|
<property name="margin-end">6</property>
|
||||||
|
<property name="margin-top">6</property>
|
||||||
|
<property name="margin-bottom">6</property>
|
||||||
|
<property name="label" translatable="yes">Select a recording of a work with multiple parts.</property>
|
||||||
|
<property name="ellipsize">end</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child type="label_item">
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
|
||||||
|
|
@ -3,60 +3,73 @@
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.24"/>
|
<requires lib="gtk+" version="3.24"/>
|
||||||
<requires lib="libhandy" version="0.0"/>
|
<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="can-focus">False</property>
|
||||||
<property name="modal">True</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="default-width">400</property>
|
|
||||||
<property name="default-height">300</property>
|
|
||||||
<property name="type-hint">dialog</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="HdyHeaderBar">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="title" translatable="yes">Tracks</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="HdyHeaderBar">
|
<object class="GtkButton" id="save_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="sensitive">False</property>
|
||||||
<property name="title" translatable="yes">Tracks</property>
|
<property name="can-focus">True</property>
|
||||||
|
<property name="receives-default">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="save_button">
|
<object class="GtkImage">
|
||||||
<property name="label" translatable="yes">Save</property>
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="sensitive">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="icon-name">object-select-symbolic</property>
|
||||||
<property name="receives-default">True</property>
|
|
||||||
<style>
|
|
||||||
<class name="suggested-action"/>
|
|
||||||
</style>
|
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="pack-type">end</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="pack-type">end</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="back_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="receives-default">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="cancel_button">
|
<object class="GtkImage">
|
||||||
<property name="label" translatable="yes">Cancel</property>
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="icon-name">go-previous-symbolic</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="position">1</property>
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="HdyClamp">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<property name="maximum-size">800</property>
|
||||||
|
<property name="tightening-threshold">300</property>
|
||||||
<child>
|
<child>
|
||||||
<!-- n-columns=2 n-rows=2 -->
|
<!-- n-columns=2 n-rows=2 -->
|
||||||
<object class="GtkGrid">
|
<object class="GtkGrid">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
<property name="border-width">18</property>
|
<property name="border-width">18</property>
|
||||||
<property name="row-spacing">12</property>
|
<property name="row-spacing">12</property>
|
||||||
<property name="column-spacing">6</property>
|
<property name="column-spacing">6</property>
|
||||||
|
|
@ -286,13 +299,13 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<!-- Generated with glade 3.38.1 -->
|
<!-- Generated with glade 3.38.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<requires lib="libhandy" version="0.0"/>
|
<requires lib="libhandy" version="1.0"/>
|
||||||
<object class="GtkStack" id="widget">
|
<object class="GtkStack" id="widget">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
|
|
@ -17,20 +17,32 @@
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="title" translatable="yes">Work</property>
|
<property name="title" translatable="yes">Work</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="cancel_button">
|
<object class="GtkButton" id="back_button">
|
||||||
<property name="label" translatable="yes">Cancel</property>
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="receives-default">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">go-previous-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="save_button">
|
<object class="GtkButton" id="save_button">
|
||||||
<property name="label" translatable="yes">Save</property>
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="receives-default">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">object-select-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<style>
|
<style>
|
||||||
<class name="suggested-action"/>
|
<class name="suggested-action"/>
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -88,91 +100,99 @@
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="show-border">False</property>
|
<property name="show-border">False</property>
|
||||||
<child>
|
<child>
|
||||||
<!-- n-columns=2 n-rows=3 -->
|
<object class="HdyClamp">
|
||||||
<object class="GtkGrid">
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="border-width">18</property>
|
<property name="maximum-size">500</property>
|
||||||
<property name="row-spacing">12</property>
|
<property name="tightening-threshold">300</property>
|
||||||
<property name="column-spacing">6</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="composer_button">
|
<!-- n-columns=2 n-rows=3 -->
|
||||||
|
<object class="GtkGrid">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="border-width">18</property>
|
||||||
<property name="hexpand">True</property>
|
<property name="row-spacing">12</property>
|
||||||
|
<property name="column-spacing">6</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="composer_label">
|
<object class="GtkButton" id="composer_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="composer_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">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="halign">start</property>
|
<property name="halign">end</property>
|
||||||
<property name="label" translatable="yes">Select …</property>
|
<property name="label" translatable="yes">Composer</property>
|
||||||
<property name="ellipsize">end</property>
|
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left-attach">0</property>
|
||||||
|
<property name="top-attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="title_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left-attach">1</property>
|
||||||
|
<property name="top-attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="composer_labe">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="halign">end</property>
|
||||||
|
<property name="label" translatable="yes">Title</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left-attach">0</property>
|
||||||
|
<property name="top-attach">0</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">Publish</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left-attach">0</property>
|
||||||
|
<property name="top-attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSwitch" id="upload_switch">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="active">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left-attach">1</property>
|
||||||
|
<property name="top-attach">2</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</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">Composer</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
<property name="top-attach">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEntry" id="title_entry">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">1</property>
|
|
||||||
<property name="top-attach">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="composer_labe">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="halign">end</property>
|
|
||||||
<property name="label" translatable="yes">Title</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
<property name="top-attach">0</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">Publish</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
<property name="top-attach">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSwitch" id="upload_switch">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">True</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="active">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">1</property>
|
|
||||||
<property name="top-attach">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
@ -187,63 +207,76 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="HdyClamp">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="border-width">18</property>
|
<property name="maximum-size">800</property>
|
||||||
<property name="spacing">6</property>
|
<property name="tightening-threshold">300</property>
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow" id="instruments_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>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="border-width">0</property>
|
<property name="border-width">18</property>
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">6</property>
|
<property name="spacing">6</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="add_instrument_button">
|
<object class="GtkScrolledWindow" id="instruments_scroll">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="shadow-type">in</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage">
|
<placeholder/>
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="icon-name">list-add-symbolic</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">True</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">True</property>
|
||||||
<property name="position">0</property>
|
<property name="position">0</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="remove_instrument_button">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="border-width">0</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage">
|
<object class="GtkButton" id="add_instrument_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="icon-name">list-remove-symbolic</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>
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="remove_instrument_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">1</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
|
|
@ -253,11 +286,6 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
|
|
@ -276,62 +304,153 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="HdyClamp">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="border-width">18</property>
|
<property name="maximum-size">800</property>
|
||||||
<property name="spacing">6</property>
|
<property name="tightening-threshold">300</property>
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow" id="structure_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>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="border-width">18</property>
|
||||||
<property name="spacing">6</property>
|
<property name="spacing">6</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="add_part_button">
|
<object class="GtkScrolledWindow" id="structure_scroll">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="shadow-type">in</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage">
|
<placeholder/>
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="icon-name">list-add-symbolic</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">True</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">True</property>
|
||||||
<property name="position">0</property>
|
<property name="position">0</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="add_section_button">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage">
|
<object class="GtkButton" id="add_part_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="icon-name">folder-new-symbolic</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>
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="add_section_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">folder-new-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="edit_part_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">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="remove_part_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">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="move_part_down_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">go-down-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="pack-type">end</property>
|
||||||
|
<property name="position">4</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="move_part_up_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">go-up-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="pack-type">end</property>
|
||||||
|
<property name="position">5</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
|
|
@ -340,90 +459,7 @@
|
||||||
<property name="position">1</property>
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="edit_part_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">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="remove_part_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">3</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="move_part_down_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">go-down-symbolic</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="pack-type">end</property>
|
|
||||||
<property name="position">4</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="move_part_up_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">go-up-symbolic</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="pack-type">end</property>
|
|
||||||
<property name="position">5</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
|
|
|
||||||
|
|
@ -2,53 +2,64 @@
|
||||||
<!-- Generated with glade 3.38.1 -->
|
<!-- Generated with glade 3.38.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<requires lib="libhandy" version="0.0"/>
|
<requires lib="libhandy" version="1.0"/>
|
||||||
<object class="HdyWindow" id="window">
|
<object class="GtkBox" id="widget">
|
||||||
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="modal">True</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="default-width">350</property>
|
|
||||||
<property name="destroy-with-parent">True</property>
|
|
||||||
<property name="type-hint">dialog</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="HdyHeaderBar">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="title" translatable="yes">Work part</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="HdyHeaderBar">
|
<object class="GtkButton" id="back_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="title" translatable="yes">Work part</property>
|
<property name="receives-default">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="cancel_button">
|
<object class="GtkImage">
|
||||||
<property name="label" translatable="yes">Cancel</property>
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="icon-name">go-previous-symbolic</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
</object>
|
||||||
<object class="GtkButton" id="save_button">
|
</child>
|
||||||
<property name="label" translatable="yes">Save</property>
|
<child>
|
||||||
<property name="visible">True</property>
|
<object class="GtkButton" id="save_button">
|
||||||
<property name="can-focus">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="can-focus">True</property>
|
||||||
<style>
|
<property name="receives-default">True</property>
|
||||||
<class name="suggested-action"/>
|
<child>
|
||||||
</style>
|
<object class="GtkImage">
|
||||||
</object>
|
<property name="visible">True</property>
|
||||||
<packing>
|
<property name="can-focus">False</property>
|
||||||
<property name="pack-type">end</property>
|
<property name="icon-name">object-select-symbolic</property>
|
||||||
<property name="position">1</property>
|
</object>
|
||||||
</packing>
|
</child>
|
||||||
</child>
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="pack-type">end</property>
|
||||||
<property name="fill">True</property>
|
<property name="position">1</property>
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="HdyClamp">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="maximum-size">500</property>
|
||||||
|
<property name="tightening-threshold">300</property>
|
||||||
<child>
|
<child>
|
||||||
<!-- n-columns=2 n-rows=2 -->
|
<!-- n-columns=2 n-rows=2 -->
|
||||||
<object class="GtkGrid">
|
<object class="GtkGrid">
|
||||||
|
|
@ -146,13 +157,13 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
@ -2,52 +2,64 @@
|
||||||
<!-- Generated with glade 3.38.1 -->
|
<!-- Generated with glade 3.38.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<requires lib="libhandy" version="0.0"/>
|
<requires lib="libhandy" version="1.0"/>
|
||||||
<object class="HdyWindow" id="window">
|
<object class="GtkBox" id="widget">
|
||||||
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="modal">True</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="destroy-with-parent">True</property>
|
|
||||||
<property name="type-hint">dialog</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="HdyHeaderBar">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="title" translatable="yes">Work section</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="HdyHeaderBar">
|
<object class="GtkButton" id="back_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="title" translatable="yes">Work section</property>
|
<property name="receives-default">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="cancel_button">
|
<object class="GtkImage">
|
||||||
<property name="label" translatable="yes">Cancel</property>
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="icon-name">go-previous-symbolic</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
</object>
|
||||||
<object class="GtkButton" id="save_button">
|
</child>
|
||||||
<property name="label" translatable="yes">Save</property>
|
<child>
|
||||||
<property name="visible">True</property>
|
<object class="GtkButton" id="save_button">
|
||||||
<property name="can-focus">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="can-focus">True</property>
|
||||||
<style>
|
<property name="receives-default">True</property>
|
||||||
<class name="suggested-action"/>
|
<child>
|
||||||
</style>
|
<object class="GtkImage">
|
||||||
</object>
|
<property name="visible">True</property>
|
||||||
<packing>
|
<property name="can-focus">False</property>
|
||||||
<property name="pack-type">end</property>
|
<property name="icon-name">object-select-symbolic</property>
|
||||||
<property name="position">1</property>
|
</object>
|
||||||
</packing>
|
</child>
|
||||||
</child>
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="pack-type">end</property>
|
||||||
<property name="fill">True</property>
|
<property name="position">1</property>
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="HdyClamp">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="maximum-size">500</property>
|
||||||
|
<property name="tightening-threshold">300</property>
|
||||||
<child>
|
<child>
|
||||||
<!-- n-columns=2 n-rows=1 -->
|
<!-- n-columns=2 n-rows=1 -->
|
||||||
<object class="GtkGrid">
|
<object class="GtkGrid">
|
||||||
|
|
@ -80,13 +92,13 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
@ -49,6 +49,20 @@
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="title" translatable="yes">Select a work</property>
|
<property name="title" translatable="yes">Select a work</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="back_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">go-previous-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="add_button">
|
<object class="GtkButton" id="add_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|
@ -62,6 +76,10 @@
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="pack-type">end</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
|
|
|
||||||
|
|
@ -1,162 +0,0 @@
|
||||||
use super::EnsembleEditor;
|
|
||||||
use crate::backend::Backend;
|
|
||||||
use crate::database::Ensemble;
|
|
||||||
use crate::widgets::List;
|
|
||||||
use gettextrs::gettext;
|
|
||||||
use gio::prelude::*;
|
|
||||||
use glib::clone;
|
|
||||||
use gtk::prelude::*;
|
|
||||||
use gtk_macros::get_widget;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
/// A dialog for selecting a ensemble.
|
|
||||||
pub struct EnsembleSelector {
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
window: libhandy::Window,
|
|
||||||
server_check_button: gtk::CheckButton,
|
|
||||||
stack: gtk::Stack,
|
|
||||||
list: Rc<List<Ensemble>>,
|
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Ensemble) -> ()>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EnsembleSelector {
|
|
||||||
pub fn new<P>(backend: Rc<Backend>, parent: &P) -> Rc<Self>
|
|
||||||
where
|
|
||||||
P: IsA<gtk::Window>,
|
|
||||||
{
|
|
||||||
// Create UI
|
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/ensemble_selector.ui");
|
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
|
||||||
get_widget!(builder, gtk::Button, add_button);
|
|
||||||
get_widget!(builder, gtk::CheckButton, server_check_button);
|
|
||||||
get_widget!(builder, gtk::SearchEntry, search_entry);
|
|
||||||
get_widget!(builder, gtk::Stack, stack);
|
|
||||||
get_widget!(builder, gtk::ScrolledWindow, scroll);
|
|
||||||
get_widget!(builder, gtk::Button, try_again_button);
|
|
||||||
|
|
||||||
window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
let list = List::<Ensemble>::new(&gettext("No ensembles found."));
|
|
||||||
scroll.add(&list.widget);
|
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
|
||||||
backend,
|
|
||||||
window,
|
|
||||||
server_check_button,
|
|
||||||
stack,
|
|
||||||
list,
|
|
||||||
selected_cb: RefCell::new(None),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Connect signals and callbacks
|
|
||||||
|
|
||||||
add_button.connect_clicked(clone!(@strong this => move |_| {
|
|
||||||
let editor = EnsembleEditor::new(
|
|
||||||
this.backend.clone(),
|
|
||||||
&this.window,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
editor.set_saved_cb(clone!(@strong this => move |ensemble| {
|
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
||||||
cb(ensemble);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.window.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
editor.show();
|
|
||||||
}));
|
|
||||||
|
|
||||||
search_entry.connect_search_changed(clone!(@strong this => move |_| {
|
|
||||||
this.list.invalidate_filter();
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_online = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
match clone.backend.get_ensembles().await {
|
|
||||||
Ok(ensembles) => {
|
|
||||||
clone.list.show_items(ensembles);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
clone.list.show_items(Vec::new());
|
|
||||||
clone.stack.set_visible_child_name("error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_local = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
let ensembles = clone.backend.db().get_ensembles().await.unwrap();
|
|
||||||
clone.list.show_items(ensembles);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.server_check_button.connect_toggled(
|
|
||||||
clone!(@strong this, @strong load_local, @strong load_online => move |_| {
|
|
||||||
if this.server_check_button.get_active() {
|
|
||||||
load_online();
|
|
||||||
} else {
|
|
||||||
load_local();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.list.set_make_widget(|ensemble: &Ensemble| {
|
|
||||||
let label = gtk::Label::new(Some(&ensemble.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);
|
|
||||||
label.upcast()
|
|
||||||
});
|
|
||||||
|
|
||||||
this.list
|
|
||||||
.set_filter(clone!(@strong search_entry => move |ensemble: &Ensemble| {
|
|
||||||
let search = search_entry.get_text().to_string().to_lowercase();
|
|
||||||
search.is_empty() || ensemble.name.contains(&search)
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.list.set_selected(clone!(@strong this => move |work| {
|
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
||||||
cb(work.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.window.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
try_again_button.connect_clicked(clone!(@strong load_online => move |_| {
|
|
||||||
load_online();
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
load_online();
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the closure to be called when the user has selected a ensemble.
|
|
||||||
pub fn set_selected_cb<F: Fn(Ensemble) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show the ensemble selector.
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,162 +0,0 @@
|
||||||
use super::InstrumentEditor;
|
|
||||||
use crate::backend::Backend;
|
|
||||||
use crate::database::Instrument;
|
|
||||||
use crate::widgets::List;
|
|
||||||
use gettextrs::gettext;
|
|
||||||
use gio::prelude::*;
|
|
||||||
use glib::clone;
|
|
||||||
use gtk::prelude::*;
|
|
||||||
use gtk_macros::get_widget;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
/// A dialog for selecting a instrument.
|
|
||||||
pub struct InstrumentSelector {
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
window: libhandy::Window,
|
|
||||||
server_check_button: gtk::CheckButton,
|
|
||||||
stack: gtk::Stack,
|
|
||||||
list: Rc<List<Instrument>>,
|
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Instrument) -> ()>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InstrumentSelector {
|
|
||||||
pub fn new<P>(backend: Rc<Backend>, parent: &P) -> Rc<Self>
|
|
||||||
where
|
|
||||||
P: IsA<gtk::Window>,
|
|
||||||
{
|
|
||||||
// Create UI
|
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/instrument_selector.ui");
|
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
|
||||||
get_widget!(builder, gtk::Button, add_button);
|
|
||||||
get_widget!(builder, gtk::CheckButton, server_check_button);
|
|
||||||
get_widget!(builder, gtk::SearchEntry, search_entry);
|
|
||||||
get_widget!(builder, gtk::Stack, stack);
|
|
||||||
get_widget!(builder, gtk::ScrolledWindow, scroll);
|
|
||||||
get_widget!(builder, gtk::Button, try_again_button);
|
|
||||||
|
|
||||||
window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
let list = List::<Instrument>::new(&gettext("No instruments found."));
|
|
||||||
scroll.add(&list.widget);
|
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
|
||||||
backend,
|
|
||||||
window,
|
|
||||||
server_check_button,
|
|
||||||
stack,
|
|
||||||
list,
|
|
||||||
selected_cb: RefCell::new(None),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Connect signals and callbacks
|
|
||||||
|
|
||||||
add_button.connect_clicked(clone!(@strong this => move |_| {
|
|
||||||
let editor = InstrumentEditor::new(
|
|
||||||
this.backend.clone(),
|
|
||||||
&this.window,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
editor.set_saved_cb(clone!(@strong this => move |instrument| {
|
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
||||||
cb(instrument);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.window.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
editor.show();
|
|
||||||
}));
|
|
||||||
|
|
||||||
search_entry.connect_search_changed(clone!(@strong this => move |_| {
|
|
||||||
this.list.invalidate_filter();
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_online = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
match clone.backend.get_instruments().await {
|
|
||||||
Ok(instruments) => {
|
|
||||||
clone.list.show_items(instruments);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
clone.list.show_items(Vec::new());
|
|
||||||
clone.stack.set_visible_child_name("error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_local = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
let instruments = clone.backend.db().get_instruments().await.unwrap();
|
|
||||||
clone.list.show_items(instruments);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.server_check_button.connect_toggled(
|
|
||||||
clone!(@strong this, @strong load_local, @strong load_online => move |_| {
|
|
||||||
if this.server_check_button.get_active() {
|
|
||||||
load_online();
|
|
||||||
} else {
|
|
||||||
load_local();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.list.set_make_widget(|instrument: &Instrument| {
|
|
||||||
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);
|
|
||||||
label.upcast()
|
|
||||||
});
|
|
||||||
|
|
||||||
this.list
|
|
||||||
.set_filter(clone!(@strong search_entry => move |instrument: &Instrument| {
|
|
||||||
let search = search_entry.get_text().to_string().to_lowercase();
|
|
||||||
search.is_empty() || instrument.name.contains(&search)
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.list.set_selected(clone!(@strong this => move |work| {
|
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
||||||
cb(work.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.window.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
try_again_button.connect_clicked(clone!(@strong load_online => move |_| {
|
|
||||||
load_online();
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
load_online();
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the closure to be called when the user has selected a instrument.
|
|
||||||
pub fn set_selected_cb<F: Fn(Instrument) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show the instrument selector.
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,41 +1,11 @@
|
||||||
pub mod about;
|
pub mod about;
|
||||||
pub use about::*;
|
pub use about::*;
|
||||||
|
|
||||||
pub mod ensemble_editor;
|
|
||||||
pub use ensemble_editor::*;
|
|
||||||
|
|
||||||
pub mod ensemble_selector;
|
|
||||||
pub use ensemble_selector::*;
|
|
||||||
|
|
||||||
pub mod instrument_editor;
|
|
||||||
pub use instrument_editor::*;
|
|
||||||
|
|
||||||
pub mod instrument_selector;
|
|
||||||
pub use instrument_selector::*;
|
|
||||||
|
|
||||||
pub mod login_dialog;
|
pub mod login_dialog;
|
||||||
pub use login_dialog::*;
|
pub use login_dialog::*;
|
||||||
|
|
||||||
pub mod person_editor;
|
|
||||||
pub use person_editor::*;
|
|
||||||
|
|
||||||
pub mod person_selector;
|
|
||||||
pub use person_selector::*;
|
|
||||||
|
|
||||||
pub mod preferences;
|
pub mod preferences;
|
||||||
pub use preferences::*;
|
pub use preferences::*;
|
||||||
|
|
||||||
pub mod server_dialog;
|
pub mod server_dialog;
|
||||||
pub use server_dialog::*;
|
pub use server_dialog::*;
|
||||||
|
|
||||||
pub mod recording;
|
|
||||||
pub use recording::*;
|
|
||||||
|
|
||||||
pub mod track_editor;
|
|
||||||
pub use track_editor::*;
|
|
||||||
|
|
||||||
pub mod tracks_editor;
|
|
||||||
pub use tracks_editor::*;
|
|
||||||
|
|
||||||
pub mod work;
|
|
||||||
pub use work::*;
|
|
||||||
|
|
|
||||||
|
|
@ -1,163 +0,0 @@
|
||||||
use super::PersonEditor;
|
|
||||||
use crate::backend::Backend;
|
|
||||||
use crate::database::Person;
|
|
||||||
use crate::widgets::List;
|
|
||||||
use gettextrs::gettext;
|
|
||||||
use gio::prelude::*;
|
|
||||||
use glib::clone;
|
|
||||||
use gtk::prelude::*;
|
|
||||||
use gtk_macros::get_widget;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
/// A dialog for selecting a person.
|
|
||||||
pub struct PersonSelector {
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
window: libhandy::Window,
|
|
||||||
server_check_button: gtk::CheckButton,
|
|
||||||
stack: gtk::Stack,
|
|
||||||
list: Rc<List<Person>>,
|
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Person) -> ()>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PersonSelector {
|
|
||||||
pub fn new<P>(backend: Rc<Backend>, parent: &P) -> Rc<Self>
|
|
||||||
where
|
|
||||||
P: IsA<gtk::Window>,
|
|
||||||
{
|
|
||||||
// Create UI
|
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/person_selector.ui");
|
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
|
||||||
get_widget!(builder, gtk::Button, add_button);
|
|
||||||
get_widget!(builder, gtk::CheckButton, server_check_button);
|
|
||||||
get_widget!(builder, gtk::SearchEntry, search_entry);
|
|
||||||
get_widget!(builder, gtk::Stack, stack);
|
|
||||||
get_widget!(builder, gtk::ScrolledWindow, scroll);
|
|
||||||
get_widget!(builder, gtk::Button, try_again_button);
|
|
||||||
|
|
||||||
window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
let list = List::<Person>::new(&gettext("No persons found."));
|
|
||||||
scroll.add(&list.widget);
|
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
|
||||||
backend,
|
|
||||||
window,
|
|
||||||
server_check_button,
|
|
||||||
stack,
|
|
||||||
list,
|
|
||||||
selected_cb: RefCell::new(None),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Connect signals and callbacks
|
|
||||||
|
|
||||||
add_button.connect_clicked(clone!(@strong this => move |_| {
|
|
||||||
let editor = PersonEditor::new(
|
|
||||||
this.backend.clone(),
|
|
||||||
&this.window,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
editor.set_saved_cb(clone!(@strong this => move |person| {
|
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
||||||
cb(person);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.window.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
editor.show();
|
|
||||||
}));
|
|
||||||
|
|
||||||
search_entry.connect_search_changed(clone!(@strong this => move |_| {
|
|
||||||
this.list.invalidate_filter();
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_online = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
match clone.backend.get_persons().await {
|
|
||||||
Ok(persons) => {
|
|
||||||
clone.list.show_items(persons);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
clone.list.show_items(Vec::new());
|
|
||||||
clone.stack.set_visible_child_name("error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_local = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
let persons = clone.backend.db().get_persons().await.unwrap();
|
|
||||||
clone.list.show_items(persons);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.server_check_button.connect_toggled(
|
|
||||||
clone!(@strong this, @strong load_local, @strong load_online => move |_| {
|
|
||||||
if this.server_check_button.get_active() {
|
|
||||||
load_online();
|
|
||||||
} else {
|
|
||||||
load_local();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.list.set_make_widget(|person: &Person| {
|
|
||||||
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);
|
|
||||||
label.upcast()
|
|
||||||
});
|
|
||||||
|
|
||||||
this.list
|
|
||||||
.set_filter(clone!(@strong search_entry => move |person: &Person| {
|
|
||||||
let search = search_entry.get_text().to_string().to_lowercase();
|
|
||||||
let name = person.name_fl().to_lowercase();
|
|
||||||
search.is_empty() || name.contains(&search)
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.list.set_selected(clone!(@strong this => move |person| {
|
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
||||||
cb(person.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.window.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
try_again_button.connect_clicked(clone!(@strong load_online => move |_| {
|
|
||||||
load_online();
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
load_online();
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the closure to be called when the user has selected a person.
|
|
||||||
pub fn set_selected_cb<F: Fn(Person) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show the person selector.
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
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(Recording) -> ()>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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(Recording) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show the recording dialog.
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
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(Recording) -> ()>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Recording>,
|
|
||||||
) -> 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(Recording) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show the recording editor dialog.
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
use super::recording_selector_person_screen::*;
|
|
||||||
use crate::backend::Backend;
|
|
||||||
use crate::database::*;
|
|
||||||
use crate::widgets::*;
|
|
||||||
use gettextrs::gettext;
|
|
||||||
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,
|
|
||||||
server_check_button: gtk::CheckButton,
|
|
||||||
stack: gtk::Stack,
|
|
||||||
list: Rc<List<Person>>,
|
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Recording) -> ()>>>,
|
|
||||||
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::CheckButton, server_check_button);
|
|
||||||
get_widget!(builder, gtk::SearchEntry, search_entry);
|
|
||||||
get_widget!(builder, gtk::Stack, stack);
|
|
||||||
get_widget!(builder, gtk::ScrolledWindow, scroll);
|
|
||||||
get_widget!(builder, gtk::Button, try_again_button);
|
|
||||||
get_widget!(builder, gtk::Box, empty_screen);
|
|
||||||
|
|
||||||
let list = List::<Person>::new(&gettext("No persons found."));
|
|
||||||
scroll.add(&list.widget);
|
|
||||||
|
|
||||||
let navigator = Navigator::new(&empty_screen);
|
|
||||||
widget.add(&navigator.widget);
|
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
|
||||||
widget,
|
|
||||||
backend,
|
|
||||||
sidebar_box,
|
|
||||||
server_check_button,
|
|
||||||
stack,
|
|
||||||
list,
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
search_entry.connect_search_changed(clone!(@strong this => move |_| {
|
|
||||||
this.list.invalidate_filter();
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_online = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
match clone.backend.get_persons().await {
|
|
||||||
Ok(persons) => {
|
|
||||||
clone.list.show_items(persons);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
clone.list.show_items(Vec::new());
|
|
||||||
clone.stack.set_visible_child_name("error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_local = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
let persons = clone.backend.db().get_persons().await.unwrap();
|
|
||||||
clone.list.show_items(persons);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.server_check_button.connect_toggled(
|
|
||||||
clone!(@strong this, @strong load_local, @strong load_online => move |_| {
|
|
||||||
if this.server_check_button.get_active() {
|
|
||||||
load_online();
|
|
||||||
} else {
|
|
||||||
load_local();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.list.set_make_widget(|person: &Person| {
|
|
||||||
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);
|
|
||||||
label.upcast()
|
|
||||||
});
|
|
||||||
|
|
||||||
this.list
|
|
||||||
.set_filter(clone!(@strong search_entry => move |person: &Person| {
|
|
||||||
let search = search_entry.get_text().to_string().to_lowercase();
|
|
||||||
let name = person.name_fl().to_lowercase();
|
|
||||||
search.is_empty() || name.contains(&search)
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.list
|
|
||||||
.set_selected(clone!(@strong this => move |person| {
|
|
||||||
let online = this.server_check_button.get_active();
|
|
||||||
|
|
||||||
let person_screen = RecordingSelectorPersonScreen::new(
|
|
||||||
this.backend.clone(),
|
|
||||||
person.clone(),
|
|
||||||
online,
|
|
||||||
);
|
|
||||||
|
|
||||||
person_screen.set_selected_cb(clone!(@strong this => move |work| {
|
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
||||||
cb(work);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.navigator.clone().replace(person_screen);
|
|
||||||
this.widget.set_visible_child(&this.navigator.widget);
|
|
||||||
}));
|
|
||||||
|
|
||||||
try_again_button.connect_clicked(clone!(@strong load_online => move |_| {
|
|
||||||
load_online();
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.navigator.set_back_cb(clone!(@strong this => move || {
|
|
||||||
this.widget.set_visible_child(&this.sidebar_box);
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
load_online();
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the closure to be called if the 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(Recording) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,161 +0,0 @@
|
||||||
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 works and switches to a work
|
|
||||||
/// screen on selection.
|
|
||||||
pub struct RecordingSelectorPersonScreen {
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
person: Person,
|
|
||||||
online: bool,
|
|
||||||
widget: gtk::Box,
|
|
||||||
stack: gtk::Stack,
|
|
||||||
work_list: Rc<List<Work>>,
|
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Recording) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecordingSelectorPersonScreen {
|
|
||||||
/// Create a new recording selector person screen.
|
|
||||||
pub fn new(backend: Rc<Backend>, person: Person, online: bool) -> 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);
|
|
||||||
get_widget!(builder, gtk::ScrolledWindow, scroll);
|
|
||||||
get_widget!(builder, gtk::Button, try_again_button);
|
|
||||||
|
|
||||||
header.set_title(Some(&person.name_fl()));
|
|
||||||
|
|
||||||
let work_list = List::new(&gettext("No works found."));
|
|
||||||
scroll.add(&work_list.widget);
|
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
|
||||||
backend,
|
|
||||||
person,
|
|
||||||
online,
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_online = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
match clone.backend.get_works(&clone.person.id).await {
|
|
||||||
Ok(works) => {
|
|
||||||
clone.work_list.show_items(works);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
clone.work_list.show_items(Vec::new());
|
|
||||||
clone.stack.set_visible_child_name("error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_local = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
let works = clone.backend.db().get_works(&clone.person.id).await.unwrap();
|
|
||||||
clone.work_list.show_items(works);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.work_list.set_make_widget(|work: &Work| {
|
|
||||||
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(),
|
|
||||||
this.online,
|
|
||||||
);
|
|
||||||
|
|
||||||
work_screen.set_selected_cb(clone!(@strong this => move |recording| {
|
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
||||||
cb(recording);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
navigator.push(work_screen);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
try_again_button.connect_clicked(clone!(@strong load_online => move |_| {
|
|
||||||
load_online();
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
|
|
||||||
if this.online {
|
|
||||||
load_online();
|
|
||||||
} else {
|
|
||||||
load_local();
|
|
||||||
}
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets a closure to be called when the user has selected a recording.
|
|
||||||
pub fn set_selected_cb<F: Fn(Recording) -> () + '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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,155 +0,0 @@
|
||||||
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>,
|
|
||||||
work: Work,
|
|
||||||
online: bool,
|
|
||||||
widget: gtk::Box,
|
|
||||||
stack: gtk::Stack,
|
|
||||||
recording_list: Rc<List<Recording>>,
|
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Recording) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecordingSelectorWorkScreen {
|
|
||||||
/// Create a new recording selector work screen.
|
|
||||||
pub fn new(backend: Rc<Backend>, work: Work, online: bool) -> 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);
|
|
||||||
get_widget!(builder, gtk::ScrolledWindow, scroll);
|
|
||||||
get_widget!(builder, gtk::Button, try_again_button);
|
|
||||||
|
|
||||||
header.set_title(Some(&work.title));
|
|
||||||
header.set_subtitle(Some(&work.composer.name_fl()));
|
|
||||||
|
|
||||||
let recording_list = List::new(&gettext("No recordings found."));
|
|
||||||
scroll.add(&recording_list.widget);
|
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
|
||||||
backend,
|
|
||||||
work,
|
|
||||||
online,
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_online = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
match clone.backend.get_recordings_for_work(&clone.work.id).await {
|
|
||||||
Ok(recordings) => {
|
|
||||||
clone.recording_list.show_items(recordings);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
clone.recording_list.show_items(Vec::new());
|
|
||||||
clone.stack.set_visible_child_name("error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_local = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
let recordings = clone.backend.db().get_recordings_for_work(&clone.work.id).await.unwrap();
|
|
||||||
clone.recording_list.show_items(recordings);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.recording_list
|
|
||||||
.set_make_widget(|recording: &Recording| {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
try_again_button.connect_clicked(clone!(@strong load_online => move |_| {
|
|
||||||
load_online();
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
|
|
||||||
if this.online {
|
|
||||||
load_online();
|
|
||||||
} else {
|
|
||||||
load_local();
|
|
||||||
}
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets a closure to be called when the user has selected a recording.
|
|
||||||
pub fn set_selected_cb<F: Fn(Recording) -> () + '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,11 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
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<WorkSelector>,
|
|
||||||
editor: Rc<WorkEditor>,
|
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Work) -> ()>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorkDialog {
|
|
||||||
/// Create a new work 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 = 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<F: Fn(Work) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show the work dialog.
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
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<Option<Box<dyn Fn(Work) -> ()>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorkEditorDialog {
|
|
||||||
/// Create a new work editor dialog and optionally initialize it.
|
|
||||||
pub fn new<W: IsA<gtk::Window>>(
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
parent: &W,
|
|
||||||
work: Option<Work>,
|
|
||||||
) -> 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 = 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<F: Fn(Work) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.saved_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show the work editor dialog.
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
use super::work_selector_person_screen::*;
|
|
||||||
use crate::backend::Backend;
|
|
||||||
use crate::database::*;
|
|
||||||
use crate::widgets::*;
|
|
||||||
use gettextrs::gettext;
|
|
||||||
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<Backend>,
|
|
||||||
sidebar_box: gtk::Box,
|
|
||||||
server_check_button: gtk::CheckButton,
|
|
||||||
stack: gtk::Stack,
|
|
||||||
list: Rc<List<Person>>,
|
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Work) -> ()>>>,
|
|
||||||
add_cb: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
|
||||||
navigator: Rc<Navigator>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorkSelector {
|
|
||||||
/// Create a new work selector.
|
|
||||||
pub fn new(backend: Rc<Backend>) -> Rc<Self> {
|
|
||||||
// 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::CheckButton, server_check_button);
|
|
||||||
get_widget!(builder, gtk::SearchEntry, search_entry);
|
|
||||||
get_widget!(builder, gtk::Stack, stack);
|
|
||||||
get_widget!(builder, gtk::ScrolledWindow, scroll);
|
|
||||||
get_widget!(builder, gtk::Button, try_again_button);
|
|
||||||
get_widget!(builder, gtk::Box, empty_screen);
|
|
||||||
|
|
||||||
let list = List::<Person>::new(&gettext("No persons found."));
|
|
||||||
scroll.add(&list.widget);
|
|
||||||
|
|
||||||
let navigator = Navigator::new(&empty_screen);
|
|
||||||
widget.add(&navigator.widget);
|
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
|
||||||
widget,
|
|
||||||
backend,
|
|
||||||
sidebar_box,
|
|
||||||
server_check_button,
|
|
||||||
stack,
|
|
||||||
list,
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
search_entry.connect_search_changed(clone!(@strong this => move |_| {
|
|
||||||
this.list.invalidate_filter();
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_online = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
match clone.backend.get_persons().await {
|
|
||||||
Ok(persons) => {
|
|
||||||
clone.list.show_items(persons);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
clone.list.show_items(Vec::new());
|
|
||||||
clone.stack.set_visible_child_name("error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_local = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
let persons = clone.backend.db().get_persons().await.unwrap();
|
|
||||||
clone.list.show_items(persons);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.server_check_button.connect_toggled(
|
|
||||||
clone!(@strong this, @strong load_local, @strong load_online => move |_| {
|
|
||||||
if this.server_check_button.get_active() {
|
|
||||||
load_online();
|
|
||||||
} else {
|
|
||||||
load_local();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.list.set_make_widget(|person: &Person| {
|
|
||||||
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);
|
|
||||||
label.upcast()
|
|
||||||
});
|
|
||||||
|
|
||||||
this.list
|
|
||||||
.set_filter(clone!(@strong search_entry => move |person: &Person| {
|
|
||||||
let search = search_entry.get_text().to_string().to_lowercase();
|
|
||||||
let name = person.name_fl().to_lowercase();
|
|
||||||
search.is_empty() || name.contains(&search)
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.list
|
|
||||||
.set_selected(clone!(@strong this => move |person| {
|
|
||||||
let online = this.server_check_button.get_active();
|
|
||||||
|
|
||||||
let person_screen = WorkSelectorPersonScreen::new(
|
|
||||||
this.backend.clone(),
|
|
||||||
person.clone(),
|
|
||||||
online,
|
|
||||||
);
|
|
||||||
|
|
||||||
person_screen.set_selected_cb(clone!(@strong this => move |work| {
|
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
||||||
cb(work);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.navigator.clone().replace(person_screen);
|
|
||||||
this.widget.set_visible_child(&this.navigator.widget);
|
|
||||||
}));
|
|
||||||
|
|
||||||
try_again_button.connect_clicked(clone!(@strong load_online => move |_| {
|
|
||||||
load_online();
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.navigator.set_back_cb(clone!(@strong this => move || {
|
|
||||||
this.widget.set_visible_child(&this.sidebar_box);
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
load_online();
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the closure to be called if the user wants to add a new work.
|
|
||||||
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 work.
|
|
||||||
pub fn set_selected_cb<F: Fn(Work) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
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<Backend>,
|
|
||||||
person: Person,
|
|
||||||
online: bool,
|
|
||||||
widget: gtk::Box,
|
|
||||||
stack: gtk::Stack,
|
|
||||||
work_list: Rc<List<Work>>,
|
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Work) -> ()>>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorkSelectorPersonScreen {
|
|
||||||
/// Create a new work selector person screen.
|
|
||||||
pub fn new(backend: Rc<Backend>, person: Person, online: bool) -> Rc<Self> {
|
|
||||||
// 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);
|
|
||||||
get_widget!(builder, gtk::ScrolledWindow, scroll);
|
|
||||||
get_widget!(builder, gtk::Button, try_again_button);
|
|
||||||
|
|
||||||
header.set_title(Some(&person.name_fl()));
|
|
||||||
|
|
||||||
let work_list = List::new(&gettext("No works found."));
|
|
||||||
scroll.add(&work_list.widget);
|
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
|
||||||
backend,
|
|
||||||
person,
|
|
||||||
online,
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_online = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
match clone.backend.get_works(&clone.person.id).await {
|
|
||||||
Ok(works) => {
|
|
||||||
clone.work_list.show_items(works);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
clone.work_list.show_items(Vec::new());
|
|
||||||
clone.stack.set_visible_child_name("error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
let load_local = Rc::new(clone!(@strong this => move || {
|
|
||||||
this.stack.set_visible_child_name("loading");
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = this.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
let works = clone.backend.db().get_works(&clone.person.id).await.unwrap();
|
|
||||||
clone.work_list.show_items(works);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.work_list.set_make_widget(|work: &Work| {
|
|
||||||
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| {
|
|
||||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
|
||||||
cb(work.clone());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
try_again_button.connect_clicked(clone!(@strong load_online => move |_| {
|
|
||||||
load_online();
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
|
|
||||||
if this.online {
|
|
||||||
load_online();
|
|
||||||
} else {
|
|
||||||
load_local();
|
|
||||||
}
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets a closure to be called when the user has selected a work.
|
|
||||||
pub fn set_selected_cb<F: Fn(Work) -> () + 'static>(&self, cb: F) {
|
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NavigatorScreen for WorkSelectorPersonScreen {
|
|
||||||
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,5 +1,6 @@
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -11,29 +12,24 @@ use std::rc::Rc;
|
||||||
pub struct EnsembleEditor {
|
pub struct EnsembleEditor {
|
||||||
backend: Rc<Backend>,
|
backend: Rc<Backend>,
|
||||||
id: String,
|
id: String,
|
||||||
window: libhandy::Window,
|
widget: gtk::Stack,
|
||||||
stack: gtk::Stack,
|
|
||||||
info_bar: gtk::InfoBar,
|
info_bar: gtk::InfoBar,
|
||||||
name_entry: gtk::Entry,
|
name_entry: gtk::Entry,
|
||||||
upload_switch: gtk::Switch,
|
upload_switch: gtk::Switch,
|
||||||
saved_cb: RefCell<Option<Box<dyn Fn(Ensemble) -> ()>>>,
|
saved_cb: RefCell<Option<Box<dyn Fn(Ensemble) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnsembleEditor {
|
impl EnsembleEditor {
|
||||||
/// Create a new ensemble editor and optionally initialize it.
|
/// Create a new ensemble editor and optionally initialize it.
|
||||||
pub fn new<P: IsA<gtk::Window>>(
|
pub fn new(backend: Rc<Backend>, ensemble: Option<Ensemble>) -> Rc<Self> {
|
||||||
backend: Rc<Backend>,
|
|
||||||
parent: &P,
|
|
||||||
ensemble: Option<Ensemble>,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/ensemble_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/ensemble_editor.ui");
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
get_widget!(builder, gtk::Stack, widget);
|
||||||
get_widget!(builder, gtk::Button, cancel_button);
|
get_widget!(builder, gtk::Button, back_button);
|
||||||
get_widget!(builder, gtk::Button, save_button);
|
get_widget!(builder, gtk::Button, save_button);
|
||||||
get_widget!(builder, gtk::Stack, stack);
|
|
||||||
get_widget!(builder, gtk::InfoBar, info_bar);
|
get_widget!(builder, gtk::InfoBar, info_bar);
|
||||||
get_widget!(builder, gtk::Entry, name_entry);
|
get_widget!(builder, gtk::Entry, name_entry);
|
||||||
get_widget!(builder, gtk::Switch, upload_switch);
|
get_widget!(builder, gtk::Switch, upload_switch);
|
||||||
|
|
@ -50,40 +46,44 @@ impl EnsembleEditor {
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
backend,
|
||||||
id,
|
id,
|
||||||
window,
|
widget,
|
||||||
stack,
|
|
||||||
info_bar,
|
info_bar,
|
||||||
name_entry,
|
name_entry,
|
||||||
upload_switch,
|
upload_switch,
|
||||||
saved_cb: RefCell::new(None),
|
saved_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
cancel_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
this.window.close();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
save_button.connect_clicked(clone!(@strong this => move |_| {
|
save_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let context = glib::MainContext::default();
|
let context = glib::MainContext::default();
|
||||||
let clone = this.clone();
|
let clone = this.clone();
|
||||||
context.spawn_local(async move {
|
context.spawn_local(async move {
|
||||||
clone.stack.set_visible_child_name("loading");
|
clone.widget.set_visible_child_name("loading");
|
||||||
match clone.clone().save().await {
|
match clone.clone().save().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
clone.window.close();
|
let navigator = clone.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
clone.info_bar.set_revealed(true);
|
clone.info_bar.set_revealed(true);
|
||||||
clone.stack.set_visible_child_name("content");
|
clone.widget.set_visible_child_name("content");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,11 +92,6 @@ impl EnsembleEditor {
|
||||||
self.saved_cb.replace(Some(Box::new(cb)));
|
self.saved_cb.replace(Some(Box::new(cb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show the ensemble editor.
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Save the ensemble and possibly upload it to the server.
|
/// Save the ensemble and possibly upload it to the server.
|
||||||
async fn save(self: Rc<Self>) -> Result<()> {
|
async fn save(self: Rc<Self>) -> Result<()> {
|
||||||
let name = self.name_entry.get_text().to_string();
|
let name = self.name_entry.get_text().to_string();
|
||||||
|
|
@ -111,7 +106,10 @@ impl EnsembleEditor {
|
||||||
self.backend.post_ensemble(&ensemble).await?;
|
self.backend.post_ensemble(&ensemble).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.backend.db().update_ensemble(ensemble.clone()).await?;
|
self.backend
|
||||||
|
.db()
|
||||||
|
.update_ensemble(ensemble.clone())
|
||||||
|
.await?;
|
||||||
self.backend.library_changed();
|
self.backend.library_changed();
|
||||||
|
|
||||||
if let Some(cb) = &*self.saved_cb.borrow() {
|
if let Some(cb) = &*self.saved_cb.borrow() {
|
||||||
|
|
@ -121,3 +119,17 @@ impl EnsembleEditor {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for EnsembleEditor {
|
||||||
|
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,5 +1,6 @@
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -11,29 +12,24 @@ use std::rc::Rc;
|
||||||
pub struct InstrumentEditor {
|
pub struct InstrumentEditor {
|
||||||
backend: Rc<Backend>,
|
backend: Rc<Backend>,
|
||||||
id: String,
|
id: String,
|
||||||
window: libhandy::Window,
|
widget: gtk::Stack,
|
||||||
stack: gtk::Stack,
|
|
||||||
info_bar: gtk::InfoBar,
|
info_bar: gtk::InfoBar,
|
||||||
name_entry: gtk::Entry,
|
name_entry: gtk::Entry,
|
||||||
upload_switch: gtk::Switch,
|
upload_switch: gtk::Switch,
|
||||||
saved_cb: RefCell<Option<Box<dyn Fn(Instrument) -> ()>>>,
|
saved_cb: RefCell<Option<Box<dyn Fn(Instrument) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstrumentEditor {
|
impl InstrumentEditor {
|
||||||
/// Create a new instrument editor and optionally initialize it.
|
/// Create a new instrument editor and optionally initialize it.
|
||||||
pub fn new<P: IsA<gtk::Window>>(
|
pub fn new(backend: Rc<Backend>, instrument: Option<Instrument>) -> Rc<Self> {
|
||||||
backend: Rc<Backend>,
|
|
||||||
parent: &P,
|
|
||||||
instrument: Option<Instrument>,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/instrument_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/instrument_editor.ui");
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
get_widget!(builder, gtk::Stack, widget);
|
||||||
get_widget!(builder, gtk::Button, cancel_button);
|
get_widget!(builder, gtk::Button, back_button);
|
||||||
get_widget!(builder, gtk::Button, save_button);
|
get_widget!(builder, gtk::Button, save_button);
|
||||||
get_widget!(builder, gtk::Stack, stack);
|
|
||||||
get_widget!(builder, gtk::InfoBar, info_bar);
|
get_widget!(builder, gtk::InfoBar, info_bar);
|
||||||
get_widget!(builder, gtk::Entry, name_entry);
|
get_widget!(builder, gtk::Entry, name_entry);
|
||||||
get_widget!(builder, gtk::Switch, upload_switch);
|
get_widget!(builder, gtk::Switch, upload_switch);
|
||||||
|
|
@ -50,40 +46,44 @@ impl InstrumentEditor {
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
backend,
|
||||||
id,
|
id,
|
||||||
window,
|
widget,
|
||||||
stack,
|
|
||||||
info_bar,
|
info_bar,
|
||||||
name_entry,
|
name_entry,
|
||||||
upload_switch,
|
upload_switch,
|
||||||
saved_cb: RefCell::new(None),
|
saved_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
cancel_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
this.window.close();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
save_button.connect_clicked(clone!(@strong this => move |_| {
|
save_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let context = glib::MainContext::default();
|
let context = glib::MainContext::default();
|
||||||
let clone = this.clone();
|
let clone = this.clone();
|
||||||
context.spawn_local(async move {
|
context.spawn_local(async move {
|
||||||
clone.stack.set_visible_child_name("loading");
|
clone.widget.set_visible_child_name("loading");
|
||||||
match clone.clone().save().await {
|
match clone.clone().save().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
clone.window.close();
|
let navigator = clone.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
clone.info_bar.set_revealed(true);
|
clone.info_bar.set_revealed(true);
|
||||||
clone.stack.set_visible_child_name("content");
|
clone.widget.set_visible_child_name("content");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,11 +92,6 @@ impl InstrumentEditor {
|
||||||
self.saved_cb.replace(Some(Box::new(cb)));
|
self.saved_cb.replace(Some(Box::new(cb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show the instrument editor.
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Save the instrument and possibly upload it to the server.
|
/// Save the instrument and possibly upload it to the server.
|
||||||
async fn save(self: Rc<Self>) -> Result<()> {
|
async fn save(self: Rc<Self>) -> Result<()> {
|
||||||
let name = self.name_entry.get_text().to_string();
|
let name = self.name_entry.get_text().to_string();
|
||||||
|
|
@ -111,7 +106,10 @@ impl InstrumentEditor {
|
||||||
self.backend.post_instrument(&instrument).await?;
|
self.backend.post_instrument(&instrument).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.backend.db().update_instrument(instrument.clone()).await?;
|
self.backend
|
||||||
|
.db()
|
||||||
|
.update_instrument(instrument.clone())
|
||||||
|
.await?;
|
||||||
self.backend.library_changed();
|
self.backend.library_changed();
|
||||||
|
|
||||||
if let Some(cb) = &*self.saved_cb.borrow() {
|
if let Some(cb) = &*self.saved_cb.borrow() {
|
||||||
|
|
@ -121,3 +119,17 @@ impl InstrumentEditor {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for InstrumentEditor {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
musicus/src/editors/mod.rs
Normal file
22
musicus/src/editors/mod.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
pub mod ensemble;
|
||||||
|
pub use ensemble::*;
|
||||||
|
|
||||||
|
pub mod instrument;
|
||||||
|
pub use instrument::*;
|
||||||
|
|
||||||
|
pub mod person;
|
||||||
|
pub use person::*;
|
||||||
|
|
||||||
|
pub mod recording;
|
||||||
|
pub use recording::*;
|
||||||
|
|
||||||
|
pub mod tracks;
|
||||||
|
pub use tracks::*;
|
||||||
|
|
||||||
|
pub mod work;
|
||||||
|
pub use work::*;
|
||||||
|
|
||||||
|
mod performance;
|
||||||
|
mod track;
|
||||||
|
mod work_part;
|
||||||
|
mod work_section;
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::backend::*;
|
use crate::backend::Backend;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::dialogs::*;
|
use crate::selectors::{EnsembleSelector, InstrumentSelector, PersonSelector};
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -11,7 +12,7 @@ use std::rc::Rc;
|
||||||
/// A dialog for editing a performance within a recording.
|
/// A dialog for editing a performance within a recording.
|
||||||
pub struct PerformanceEditor {
|
pub struct PerformanceEditor {
|
||||||
backend: Rc<Backend>,
|
backend: Rc<Backend>,
|
||||||
window: libhandy::Window,
|
widget: gtk::Box,
|
||||||
save_button: gtk::Button,
|
save_button: gtk::Button,
|
||||||
person_label: gtk::Label,
|
person_label: gtk::Label,
|
||||||
ensemble_label: gtk::Label,
|
ensemble_label: gtk::Label,
|
||||||
|
|
@ -21,21 +22,18 @@ pub struct PerformanceEditor {
|
||||||
ensemble: RefCell<Option<Ensemble>>,
|
ensemble: RefCell<Option<Ensemble>>,
|
||||||
role: RefCell<Option<Instrument>>,
|
role: RefCell<Option<Instrument>>,
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Performance) -> ()>>>,
|
selected_cb: RefCell<Option<Box<dyn Fn(Performance) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PerformanceEditor {
|
impl PerformanceEditor {
|
||||||
/// Create a new performance editor.
|
/// Create a new performance editor.
|
||||||
pub fn new<P: IsA<gtk::Window>>(
|
pub fn new(backend: Rc<Backend>, performance: Option<Performance>) -> Rc<Self> {
|
||||||
backend: Rc<Backend>,
|
|
||||||
parent: &P,
|
|
||||||
performance: Option<Performance>,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/performance_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/performance_editor.ui");
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
get_widget!(builder, gtk::Box, widget);
|
||||||
get_widget!(builder, gtk::Button, cancel_button);
|
get_widget!(builder, gtk::Button, back_button);
|
||||||
get_widget!(builder, gtk::Button, save_button);
|
get_widget!(builder, gtk::Button, save_button);
|
||||||
get_widget!(builder, gtk::Button, person_button);
|
get_widget!(builder, gtk::Button, person_button);
|
||||||
get_widget!(builder, gtk::Button, ensemble_button);
|
get_widget!(builder, gtk::Button, ensemble_button);
|
||||||
|
|
@ -45,11 +43,9 @@ impl PerformanceEditor {
|
||||||
get_widget!(builder, gtk::Label, ensemble_label);
|
get_widget!(builder, gtk::Label, ensemble_label);
|
||||||
get_widget!(builder, gtk::Label, role_label);
|
get_widget!(builder, gtk::Label, role_label);
|
||||||
|
|
||||||
window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
let this = Rc::new(PerformanceEditor {
|
let this = Rc::new(PerformanceEditor {
|
||||||
backend,
|
backend,
|
||||||
window,
|
widget,
|
||||||
save_button,
|
save_button,
|
||||||
person_label,
|
person_label,
|
||||||
ensemble_label,
|
ensemble_label,
|
||||||
|
|
@ -59,12 +55,16 @@ impl PerformanceEditor {
|
||||||
ensemble: RefCell::new(None),
|
ensemble: RefCell::new(None),
|
||||||
role: RefCell::new(None),
|
role: RefCell::new(None),
|
||||||
selected_cb: RefCell::new(None),
|
selected_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
cancel_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
this.window.close();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.save_button
|
this.save_button
|
||||||
|
|
@ -75,46 +75,61 @@ impl PerformanceEditor {
|
||||||
ensemble: this.ensemble.borrow().clone(),
|
ensemble: this.ensemble.borrow().clone(),
|
||||||
role: this.role.borrow().clone(),
|
role: this.role.borrow().clone(),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.window.close();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
person_button.connect_clicked(clone!(@strong this => move |_| {
|
person_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let dialog = PersonSelector::new(this.backend.clone(), &this.window);
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let selector = PersonSelector::new(this.backend.clone());
|
||||||
|
|
||||||
dialog.set_selected_cb(clone!(@strong this => move |person| {
|
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |person| {
|
||||||
this.show_person(Some(&person));
|
this.show_person(Some(&person));
|
||||||
this.person.replace(Some(person));
|
this.person.replace(Some(person.clone()));
|
||||||
this.show_ensemble(None);
|
this.show_ensemble(None);
|
||||||
this.ensemble.replace(None);
|
this.ensemble.replace(None);
|
||||||
}));
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
dialog.show();
|
navigator.push(selector);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
ensemble_button.connect_clicked(clone!(@strong this => move |_| {
|
ensemble_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let dialog = EnsembleSelector::new(this.backend.clone(), &this.window);
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let selector = EnsembleSelector::new(this.backend.clone());
|
||||||
|
|
||||||
dialog.set_selected_cb(clone!(@strong this => move |ensemble| {
|
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |ensemble| {
|
||||||
this.show_person(None);
|
this.show_person(None);
|
||||||
this.person.replace(None);
|
this.person.replace(None);
|
||||||
this.show_ensemble(Some(&ensemble));
|
this.show_ensemble(Some(&ensemble));
|
||||||
this.ensemble.replace(Some(ensemble));
|
this.ensemble.replace(Some(ensemble.clone()));
|
||||||
}));
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
dialog.show();
|
navigator.push(selector);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
role_button.connect_clicked(clone!(@strong this => move |_| {
|
role_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let dialog = InstrumentSelector::new(this.backend.clone(), &this.window);
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let selector = InstrumentSelector::new(this.backend.clone());
|
||||||
|
|
||||||
dialog.set_selected_cb(clone!(@strong this => move |role| {
|
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |role| {
|
||||||
this.show_role(Some(&role));
|
this.show_role(Some(&role));
|
||||||
this.role.replace(Some(role));
|
this.role.replace(Some(role.clone()));
|
||||||
}));
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
dialog.show();
|
navigator.push(selector);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.reset_role_button
|
this.reset_role_button
|
||||||
|
|
@ -148,11 +163,6 @@ impl PerformanceEditor {
|
||||||
self.selected_cb.replace(Some(Box::new(cb)));
|
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.
|
/// Update the UI according to person.
|
||||||
fn show_person(&self, person: Option<&Person>) {
|
fn show_person(&self, person: Option<&Person>) {
|
||||||
if let Some(person) = person {
|
if let Some(person) = person {
|
||||||
|
|
@ -184,3 +194,17 @@ impl PerformanceEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for PerformanceEditor {
|
||||||
|
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,5 +1,6 @@
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -11,30 +12,25 @@ use std::rc::Rc;
|
||||||
pub struct PersonEditor {
|
pub struct PersonEditor {
|
||||||
backend: Rc<Backend>,
|
backend: Rc<Backend>,
|
||||||
id: String,
|
id: String,
|
||||||
window: libhandy::Window,
|
widget: gtk::Stack,
|
||||||
stack: gtk::Stack,
|
|
||||||
info_bar: gtk::InfoBar,
|
info_bar: gtk::InfoBar,
|
||||||
first_name_entry: gtk::Entry,
|
first_name_entry: gtk::Entry,
|
||||||
last_name_entry: gtk::Entry,
|
last_name_entry: gtk::Entry,
|
||||||
upload_switch: gtk::Switch,
|
upload_switch: gtk::Switch,
|
||||||
saved_cb: RefCell<Option<Box<dyn Fn(Person) -> ()>>>,
|
saved_cb: RefCell<Option<Box<dyn Fn(Person) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PersonEditor {
|
impl PersonEditor {
|
||||||
/// Create a new person editor and optionally initialize it.
|
/// Create a new person editor and optionally initialize it.
|
||||||
pub fn new<P: IsA<gtk::Window>>(
|
pub fn new(backend: Rc<Backend>, person: Option<Person>) -> Rc<Self> {
|
||||||
backend: Rc<Backend>,
|
|
||||||
parent: &P,
|
|
||||||
person: Option<Person>,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/person_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/person_editor.ui");
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
get_widget!(builder, gtk::Stack, widget);
|
||||||
get_widget!(builder, gtk::Button, cancel_button);
|
get_widget!(builder, gtk::Button, back_button);
|
||||||
get_widget!(builder, gtk::Button, save_button);
|
get_widget!(builder, gtk::Button, save_button);
|
||||||
get_widget!(builder, gtk::Stack, stack);
|
|
||||||
get_widget!(builder, gtk::InfoBar, info_bar);
|
get_widget!(builder, gtk::InfoBar, info_bar);
|
||||||
get_widget!(builder, gtk::Entry, first_name_entry);
|
get_widget!(builder, gtk::Entry, first_name_entry);
|
||||||
get_widget!(builder, gtk::Entry, last_name_entry);
|
get_widget!(builder, gtk::Entry, last_name_entry);
|
||||||
|
|
@ -53,41 +49,45 @@ impl PersonEditor {
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
backend,
|
||||||
id,
|
id,
|
||||||
window,
|
widget,
|
||||||
stack,
|
|
||||||
info_bar,
|
info_bar,
|
||||||
first_name_entry,
|
first_name_entry,
|
||||||
last_name_entry,
|
last_name_entry,
|
||||||
upload_switch,
|
upload_switch,
|
||||||
saved_cb: RefCell::new(None),
|
saved_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
cancel_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
this.window.close();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
save_button.connect_clicked(clone!(@strong this => move |_| {
|
save_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let context = glib::MainContext::default();
|
let context = glib::MainContext::default();
|
||||||
let clone = this.clone();
|
let clone = this.clone();
|
||||||
context.spawn_local(async move {
|
context.spawn_local(async move {
|
||||||
clone.stack.set_visible_child_name("loading");
|
clone.widget.set_visible_child_name("loading");
|
||||||
match clone.clone().save().await {
|
match clone.clone().save().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
clone.window.close();
|
let navigator = clone.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
clone.info_bar.set_revealed(true);
|
clone.info_bar.set_revealed(true);
|
||||||
clone.stack.set_visible_child_name("content");
|
clone.widget.set_visible_child_name("content");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,11 +96,6 @@ impl PersonEditor {
|
||||||
self.saved_cb.replace(Some(Box::new(cb)));
|
self.saved_cb.replace(Some(Box::new(cb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show the person editor.
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Save the person and possibly upload it to the server.
|
/// Save the person and possibly upload it to the server.
|
||||||
async fn save(self: Rc<Self>) -> Result<()> {
|
async fn save(self: Rc<Self>) -> Result<()> {
|
||||||
let first_name = self.first_name_entry.get_text().to_string();
|
let first_name = self.first_name_entry.get_text().to_string();
|
||||||
|
|
@ -127,3 +122,17 @@ impl PersonEditor {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for PersonEditor {
|
||||||
|
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,8 +1,8 @@
|
||||||
use super::performance_editor::*;
|
use super::performance::PerformanceEditor;
|
||||||
use crate::backend::*;
|
use crate::backend::Backend;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::dialogs::*;
|
use crate::selectors::{PersonSelector, WorkSelector};
|
||||||
use crate::widgets::*;
|
use crate::widgets::{List, Navigator, NavigatorScreen};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -16,7 +16,6 @@ use std::rc::Rc;
|
||||||
pub struct RecordingEditor {
|
pub struct RecordingEditor {
|
||||||
pub widget: gtk::Stack,
|
pub widget: gtk::Stack,
|
||||||
backend: Rc<Backend>,
|
backend: Rc<Backend>,
|
||||||
parent: gtk::Window,
|
|
||||||
save_button: gtk::Button,
|
save_button: gtk::Button,
|
||||||
info_bar: gtk::InfoBar,
|
info_bar: gtk::InfoBar,
|
||||||
work_label: gtk::Label,
|
work_label: gtk::Label,
|
||||||
|
|
@ -28,16 +27,12 @@ pub struct RecordingEditor {
|
||||||
performances: RefCell<Vec<Performance>>,
|
performances: RefCell<Vec<Performance>>,
|
||||||
selected_cb: RefCell<Option<Box<dyn Fn(Recording) -> ()>>>,
|
selected_cb: RefCell<Option<Box<dyn Fn(Recording) -> ()>>>,
|
||||||
back_cb: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
back_cb: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RecordingEditor {
|
impl RecordingEditor {
|
||||||
/// Create a new recording editor widget and optionally initialize it. The parent window is
|
/// Create a new recording editor widget and optionally initialize it.
|
||||||
/// used as the parent for newly created dialogs.
|
pub fn new(backend: Rc<Backend>, recording: Option<Recording>) -> Rc<Self> {
|
||||||
pub fn new<W: IsA<gtk::Window>>(
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
parent: &W,
|
|
||||||
recording: Option<Recording>,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_editor.ui");
|
||||||
|
|
@ -69,7 +64,6 @@ impl RecordingEditor {
|
||||||
let this = Rc::new(RecordingEditor {
|
let this = Rc::new(RecordingEditor {
|
||||||
widget,
|
widget,
|
||||||
backend,
|
backend,
|
||||||
parent: parent.clone().upcast(),
|
|
||||||
save_button,
|
save_button,
|
||||||
info_bar,
|
info_bar,
|
||||||
work_label,
|
work_label,
|
||||||
|
|
@ -81,6 +75,7 @@ impl RecordingEditor {
|
||||||
performances: RefCell::new(performances),
|
performances: RefCell::new(performances),
|
||||||
selected_cb: RefCell::new(None),
|
selected_cb: RefCell::new(None),
|
||||||
back_cb: RefCell::new(None),
|
back_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
@ -89,6 +84,11 @@ impl RecordingEditor {
|
||||||
if let Some(cb) = &*this.back_cb.borrow() {
|
if let Some(cb) = &*this.back_cb.borrow() {
|
||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.clone().pop();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.save_button
|
this.save_button
|
||||||
|
|
@ -99,7 +99,10 @@ impl RecordingEditor {
|
||||||
clone.widget.set_visible_child_name("loading");
|
clone.widget.set_visible_child_name("loading");
|
||||||
match clone.clone().save().await {
|
match clone.clone().save().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// We already called the callback.
|
let navigator = clone.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.clone().pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
clone.info_bar.set_revealed(true);
|
clone.info_bar.set_revealed(true);
|
||||||
|
|
@ -111,14 +114,26 @@ impl RecordingEditor {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
work_button.connect_clicked(clone!(@strong this => move |_| {
|
work_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let dialog = WorkDialog::new(this.backend.clone(), &this.parent);
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let person_selector = PersonSelector::new(this.backend.clone());
|
||||||
|
|
||||||
dialog.set_selected_cb(clone!(@strong this => move |work| {
|
person_selector.set_selected_cb(clone!(@strong this, @strong navigator => move |person| {
|
||||||
this.work_selected(&work);
|
let work_selector = WorkSelector::new(this.backend.clone(), person.clone());
|
||||||
this.work.replace(Some(work));
|
|
||||||
}));
|
|
||||||
|
|
||||||
dialog.show();
|
work_selector.set_selected_cb(clone!(@strong this, @strong navigator => move |work| {
|
||||||
|
this.work_selected(&work);
|
||||||
|
this.work.replace(Some(work.clone()));
|
||||||
|
|
||||||
|
navigator.clone().pop();
|
||||||
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
|
navigator.clone().push(work_selector);
|
||||||
|
}));
|
||||||
|
|
||||||
|
navigator.push(person_selector);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.performance_list.set_make_widget(|performance| {
|
this.performance_list.set_make_widget(|performance| {
|
||||||
|
|
@ -133,42 +148,50 @@ impl RecordingEditor {
|
||||||
});
|
});
|
||||||
|
|
||||||
add_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
add_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let editor = PerformanceEditor::new(this.backend.clone(), &this.parent, None);
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let editor = PerformanceEditor::new(this.backend.clone(), None);
|
||||||
|
|
||||||
editor.set_selected_cb(clone!(@strong this => move |performance| {
|
editor.set_selected_cb(clone!(@strong this, @strong navigator => move |performance| {
|
||||||
let mut performances = this.performances.borrow_mut();
|
let mut performances = this.performances.borrow_mut();
|
||||||
|
|
||||||
let index = match this.performance_list.get_selected_index() {
|
let index = match this.performance_list.get_selected_index() {
|
||||||
Some(index) => index + 1,
|
Some(index) => index + 1,
|
||||||
None => performances.len(),
|
None => performances.len(),
|
||||||
};
|
};
|
||||||
|
|
||||||
performances.insert(index, performance);
|
performances.insert(index, performance);
|
||||||
this.performance_list.show_items(performances.clone());
|
this.performance_list.show_items(performances.clone());
|
||||||
this.performance_list.select_index(index);
|
this.performance_list.select_index(index);
|
||||||
}));
|
|
||||||
|
|
||||||
editor.show();
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
|
navigator.push(editor);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
edit_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
edit_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
if let Some(index) = this.performance_list.get_selected_index() {
|
let navigator = this.navigator.borrow().clone();
|
||||||
let performance = &this.performances.borrow()[index];
|
if let Some(navigator) = navigator {
|
||||||
|
if let Some(index) = this.performance_list.get_selected_index() {
|
||||||
|
let performance = &this.performances.borrow()[index];
|
||||||
|
|
||||||
let editor = PerformanceEditor::new(
|
let editor = PerformanceEditor::new(
|
||||||
this.backend.clone(),
|
this.backend.clone(),
|
||||||
&this.parent,
|
Some(performance.clone()),
|
||||||
Some(performance.clone()),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
editor.set_selected_cb(clone!(@strong this => move |performance| {
|
editor.set_selected_cb(clone!(@strong this, @strong navigator => move |performance| {
|
||||||
let mut performances = this.performances.borrow_mut();
|
let mut performances = this.performances.borrow_mut();
|
||||||
performances[index] = performance;
|
performances[index] = performance;
|
||||||
this.performance_list.show_items(performances.clone());
|
this.performance_list.show_items(performances.clone());
|
||||||
this.performance_list.select_index(index);
|
this.performance_list.select_index(index);
|
||||||
}));
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
editor.show();
|
navigator.push(editor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -240,6 +263,25 @@ impl RecordingEditor {
|
||||||
cb(recording.clone());
|
cb(recording.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let navigator = self.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.clone().pop();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for RecordingEditor {
|
||||||
|
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,4 +1,5 @@
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk_macros::get_widget;
|
use gtk_macros::get_widget;
|
||||||
|
|
@ -6,43 +7,58 @@ use std::cell::RefCell;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A screen for editing a single track.
|
||||||
|
// TODO: Refactor.
|
||||||
pub struct TrackEditor {
|
pub struct TrackEditor {
|
||||||
window: libhandy::Window,
|
widget: gtk::Box,
|
||||||
|
ready_cb: RefCell<Option<Box<dyn Fn(Track) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrackEditor {
|
impl TrackEditor {
|
||||||
pub fn new<W, F>(parent: &W, track: Track, work: Work, callback: F) -> Self
|
/// Create a new track editor.
|
||||||
where
|
pub fn new(track: Track, work: Work) -> Rc<Self> {
|
||||||
W: IsA<gtk::Window>,
|
// Create UI
|
||||||
F: Fn(Track) -> () + 'static,
|
|
||||||
{
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/track_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/track_editor.ui");
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
get_widget!(builder, gtk::Box, widget);
|
||||||
get_widget!(builder, gtk::Button, cancel_button);
|
get_widget!(builder, gtk::Button, back_button);
|
||||||
get_widget!(builder, gtk::Button, save_button);
|
get_widget!(builder, gtk::Button, save_button);
|
||||||
get_widget!(builder, gtk::ListBox, list);
|
get_widget!(builder, gtk::ListBox, list);
|
||||||
|
|
||||||
window.set_transient_for(Some(parent));
|
let this = Rc::new(Self {
|
||||||
|
widget,
|
||||||
|
ready_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
cancel_button.connect_clicked(clone!(@strong window => move |_| {
|
back_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
window.close();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let work = Rc::new(work);
|
let work = Rc::new(work);
|
||||||
let work_parts = Rc::new(RefCell::new(track.work_parts));
|
let work_parts = Rc::new(RefCell::new(track.work_parts));
|
||||||
let file_name = track.file_name;
|
let file_name = track.file_name;
|
||||||
|
|
||||||
save_button.connect_clicked(clone!(@strong work_parts, @strong window => move |_| {
|
save_button.connect_clicked(clone!(@strong this, @strong work_parts => move |_| {
|
||||||
let mut work_parts = work_parts.borrow_mut();
|
let mut work_parts = work_parts.borrow_mut();
|
||||||
work_parts.sort();
|
work_parts.sort();
|
||||||
|
|
||||||
callback(Track {
|
if let Some(cb) = &*this.ready_cb.borrow() {
|
||||||
work_parts: work_parts.clone(),
|
cb(Track {
|
||||||
file_name: file_name.clone(),
|
work_parts: work_parts.clone(),
|
||||||
});
|
file_name: file_name.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
window.close();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
for (index, part) in work.parts.iter().enumerate() {
|
for (index, part) in work.parts.iter().enumerate() {
|
||||||
|
|
@ -108,10 +124,25 @@ impl TrackEditor {
|
||||||
section_count += 1;
|
section_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Self { window }
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show(&self) {
|
/// Set the closure to be called when the track was edited.
|
||||||
self.window.show();
|
pub fn set_ready_cb<F: Fn(Track) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.ready_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for TrackEditor {
|
||||||
|
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,7 +1,8 @@
|
||||||
use super::*;
|
use super::track::TrackEditor;
|
||||||
use crate::backend::*;
|
use crate::backend::Backend;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::widgets::*;
|
use crate::widgets::{List, Navigator, NavigatorScreen};
|
||||||
|
use crate::selectors::{PersonSelector, WorkSelector, RecordingSelector};
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -13,7 +14,7 @@ use std::rc::Rc;
|
||||||
// TODO: Disable buttons if no track is selected.
|
// TODO: Disable buttons if no track is selected.
|
||||||
pub struct TracksEditor {
|
pub struct TracksEditor {
|
||||||
backend: Rc<Backend>,
|
backend: Rc<Backend>,
|
||||||
window: libhandy::Window,
|
widget: gtk::Box,
|
||||||
save_button: gtk::Button,
|
save_button: gtk::Button,
|
||||||
recording_stack: gtk::Stack,
|
recording_stack: gtk::Stack,
|
||||||
work_label: gtk::Label,
|
work_label: gtk::Label,
|
||||||
|
|
@ -22,14 +23,14 @@ pub struct TracksEditor {
|
||||||
recording: RefCell<Option<Recording>>,
|
recording: RefCell<Option<Recording>>,
|
||||||
tracks: RefCell<Vec<Track>>,
|
tracks: RefCell<Vec<Track>>,
|
||||||
callback: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
callback: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TracksEditor {
|
impl TracksEditor {
|
||||||
/// Create a new track editor an optionally initialize it with a recording and a list of
|
/// Create a new track editor an optionally initialize it with a recording and a list of
|
||||||
/// tracks.
|
/// tracks.
|
||||||
pub fn new<P: IsA<gtk::Window>>(
|
pub fn new(
|
||||||
backend: Rc<Backend>,
|
backend: Rc<Backend>,
|
||||||
parent: &P,
|
|
||||||
recording: Option<Recording>,
|
recording: Option<Recording>,
|
||||||
tracks: Vec<Track>,
|
tracks: Vec<Track>,
|
||||||
) -> Rc<Self> {
|
) -> Rc<Self> {
|
||||||
|
|
@ -37,8 +38,8 @@ impl TracksEditor {
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/tracks_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/tracks_editor.ui");
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
get_widget!(builder, gtk::Box, widget);
|
||||||
get_widget!(builder, gtk::Button, cancel_button);
|
get_widget!(builder, gtk::Button, back_button);
|
||||||
get_widget!(builder, gtk::Button, save_button);
|
get_widget!(builder, gtk::Button, save_button);
|
||||||
get_widget!(builder, gtk::Button, recording_button);
|
get_widget!(builder, gtk::Button, recording_button);
|
||||||
get_widget!(builder, gtk::Stack, recording_stack);
|
get_widget!(builder, gtk::Stack, recording_stack);
|
||||||
|
|
@ -51,18 +52,12 @@ impl TracksEditor {
|
||||||
get_widget!(builder, gtk::Button, move_track_up_button);
|
get_widget!(builder, gtk::Button, move_track_up_button);
|
||||||
get_widget!(builder, gtk::Button, move_track_down_button);
|
get_widget!(builder, gtk::Button, move_track_down_button);
|
||||||
|
|
||||||
window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
cancel_button.connect_clicked(clone!(@strong window => move |_| {
|
|
||||||
window.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
let track_list = List::new(&gettext("Add some tracks."));
|
let track_list = List::new(&gettext("Add some tracks."));
|
||||||
scroll.add(&track_list.widget);
|
scroll.add(&track_list.widget);
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
backend,
|
||||||
window,
|
widget,
|
||||||
save_button,
|
save_button,
|
||||||
recording_stack,
|
recording_stack,
|
||||||
work_label,
|
work_label,
|
||||||
|
|
@ -71,10 +66,18 @@ impl TracksEditor {
|
||||||
recording: RefCell::new(recording),
|
recording: RefCell::new(recording),
|
||||||
tracks: RefCell::new(tracks),
|
tracks: RefCell::new(tracks),
|
||||||
callback: RefCell::new(None),
|
callback: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Signals and callbacks
|
// 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.save_button
|
this.save_button
|
||||||
.connect_clicked(clone!(@strong this => move |_| {
|
.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let context = glib::MainContext::default();
|
let context = glib::MainContext::default();
|
||||||
|
|
@ -99,22 +102,43 @@ impl TracksEditor {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.window.close();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
recording_button.connect_clicked(clone!(@strong this => move |_| {
|
recording_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let dialog = RecordingDialog::new(this.backend.clone(), &this.window);
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let person_selector = PersonSelector::new(this.backend.clone());
|
||||||
|
|
||||||
dialog.set_selected_cb(clone!(@strong this => move |recording| {
|
person_selector.set_selected_cb(clone!(@strong this, @strong navigator => move |person| {
|
||||||
this.recording_selected(&recording);
|
let work_selector = WorkSelector::new(this.backend.clone(), person.clone());
|
||||||
this.recording.replace(Some(recording));
|
|
||||||
}));
|
|
||||||
|
|
||||||
dialog.show();
|
work_selector.set_selected_cb(clone!(@strong this, @strong navigator => move |work| {
|
||||||
|
let recording_selector = RecordingSelector::new(this.backend.clone(), work.clone());
|
||||||
|
|
||||||
|
recording_selector.set_selected_cb(clone!(@strong this, @strong navigator => move |recording| {
|
||||||
|
this.recording_selected(recording);
|
||||||
|
this.recording.replace(Some(recording.clone()));
|
||||||
|
|
||||||
|
navigator.clone().pop();
|
||||||
|
navigator.clone().pop();
|
||||||
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
|
navigator.clone().push(recording_selector);
|
||||||
|
}));
|
||||||
|
|
||||||
|
navigator.clone().push(work_selector);
|
||||||
|
}));
|
||||||
|
|
||||||
|
navigator.clone().push(person_selector);
|
||||||
}
|
}
|
||||||
));
|
}));
|
||||||
|
|
||||||
this.track_list
|
this.track_list
|
||||||
.set_make_widget(clone!(@strong this => move |track| {
|
.set_make_widget(clone!(@strong this => move |track| {
|
||||||
|
|
@ -122,40 +146,43 @@ impl TracksEditor {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
add_track_button.connect_clicked(clone!(@strong this => move |_| {
|
add_track_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let music_library_path = this.backend.get_music_library_path().unwrap();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let music_library_path = this.backend.get_music_library_path().unwrap();
|
||||||
|
|
||||||
let dialog = gtk::FileChooserNative::new(
|
let dialog = gtk::FileChooserNative::new(
|
||||||
Some(&gettext("Select audio files")),
|
Some(&gettext("Select audio files")),
|
||||||
Some(&this.window),
|
Some(&navigator.window),
|
||||||
gtk::FileChooserAction::Open,
|
gtk::FileChooserAction::Open,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
dialog.set_select_multiple(true);
|
dialog.set_select_multiple(true);
|
||||||
dialog.set_current_folder(&music_library_path);
|
dialog.set_current_folder(&music_library_path);
|
||||||
|
|
||||||
if let gtk::ResponseType::Accept = dialog.run() {
|
if let gtk::ResponseType::Accept = dialog.run() {
|
||||||
let mut index = match this.track_list.get_selected_index() {
|
let mut index = match this.track_list.get_selected_index() {
|
||||||
Some(index) => index + 1,
|
Some(index) => index + 1,
|
||||||
None => this.tracks.borrow().len(),
|
None => this.tracks.borrow().len(),
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut tracks = this.tracks.borrow_mut();
|
let mut tracks = this.tracks.borrow_mut();
|
||||||
for file_name in dialog.get_filenames() {
|
for file_name in dialog.get_filenames() {
|
||||||
let file_name = file_name.strip_prefix(&music_library_path).unwrap();
|
let file_name = file_name.strip_prefix(&music_library_path).unwrap();
|
||||||
tracks.insert(index, Track {
|
tracks.insert(index, Track {
|
||||||
work_parts: Vec::new(),
|
work_parts: Vec::new(),
|
||||||
file_name: String::from(file_name.to_str().unwrap()),
|
file_name: String::from(file_name.to_str().unwrap()),
|
||||||
});
|
});
|
||||||
index += 1;
|
index += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.track_list.show_items(this.tracks.borrow().clone());
|
this.track_list.show_items(this.tracks.borrow().clone());
|
||||||
this.autofill_parts();
|
this.autofill_parts();
|
||||||
this.track_list.select_index(index);
|
this.track_list.select_index(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -200,14 +227,21 @@ impl TracksEditor {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
edit_track_button.connect_clicked(clone!(@strong this => move |_| {
|
edit_track_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
if let Some(index) = this.track_list.get_selected_index() {
|
let navigator = this.navigator.borrow().clone();
|
||||||
if let Some(recording) = &*this.recording.borrow() {
|
if let Some(navigator) = navigator {
|
||||||
TrackEditor::new(&this.window, this.tracks.borrow()[index].clone(), recording.work.clone(), clone!(@strong this => move |track| {
|
if let Some(index) = this.track_list.get_selected_index() {
|
||||||
let mut tracks = this.tracks.borrow_mut();
|
if let Some(recording) = &*this.recording.borrow() {
|
||||||
tracks[index] = track;
|
let editor = TrackEditor::new(this.tracks.borrow()[index].clone(), recording.work.clone());
|
||||||
this.track_list.show_items(tracks.clone());
|
|
||||||
this.track_list.select_index(index);
|
editor.set_ready_cb(clone!(@strong this => move |track| {
|
||||||
})).show();
|
let mut tracks = this.tracks.borrow_mut();
|
||||||
|
tracks[index] = track;
|
||||||
|
this.track_list.show_items(tracks.clone());
|
||||||
|
this.track_list.select_index(index);
|
||||||
|
}));
|
||||||
|
|
||||||
|
navigator.push(editor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
@ -228,11 +262,6 @@ impl TracksEditor {
|
||||||
self.callback.replace(Some(Box::new(cb)));
|
self.callback.replace(Some(Box::new(cb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open the track editor.
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a widget representing a track.
|
/// Create a widget representing a track.
|
||||||
fn build_track_row(&self, track: &Track) -> gtk::Widget {
|
fn build_track_row(&self, track: &Track) -> gtk::Widget {
|
||||||
let mut title_parts = Vec::<String>::new();
|
let mut title_parts = Vec::<String>::new();
|
||||||
|
|
@ -292,3 +321,17 @@ impl TracksEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for TracksEditor {
|
||||||
|
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,9 +1,9 @@
|
||||||
use super::part_editor::*;
|
use super::work_part::WorkPartEditor;
|
||||||
use super::section_editor::*;
|
use super::work_section::WorkSectionEditor;
|
||||||
use crate::backend::*;
|
use crate::backend::Backend;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::dialogs::*;
|
use crate::selectors::{InstrumentSelector, PersonSelector};
|
||||||
use crate::widgets::*;
|
use crate::widgets::{List, Navigator, NavigatorScreen};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -22,9 +22,8 @@ enum PartOrSection {
|
||||||
|
|
||||||
/// A widget for editing and creating works.
|
/// A widget for editing and creating works.
|
||||||
pub struct WorkEditor {
|
pub struct WorkEditor {
|
||||||
pub widget: gtk::Stack,
|
widget: gtk::Stack,
|
||||||
backend: Rc<Backend>,
|
backend: Rc<Backend>,
|
||||||
parent: gtk::Window,
|
|
||||||
save_button: gtk::Button,
|
save_button: gtk::Button,
|
||||||
title_entry: gtk::Entry,
|
title_entry: gtk::Entry,
|
||||||
info_bar: gtk::InfoBar,
|
info_bar: gtk::InfoBar,
|
||||||
|
|
@ -36,24 +35,19 @@ pub struct WorkEditor {
|
||||||
composer: RefCell<Option<Person>>,
|
composer: RefCell<Option<Person>>,
|
||||||
instruments: RefCell<Vec<Instrument>>,
|
instruments: RefCell<Vec<Instrument>>,
|
||||||
structure: RefCell<Vec<PartOrSection>>,
|
structure: RefCell<Vec<PartOrSection>>,
|
||||||
cancel_cb: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
|
||||||
saved_cb: RefCell<Option<Box<dyn Fn(Work) -> ()>>>,
|
saved_cb: RefCell<Option<Box<dyn Fn(Work) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkEditor {
|
impl WorkEditor {
|
||||||
/// Create a new work editor widget and optionally initialize it. The parent window is used
|
/// Create a new work editor widget and optionally initialize it.
|
||||||
/// as the parent for newly created dialogs.
|
pub fn new(backend: Rc<Backend>, work: Option<Work>) -> Rc<Self> {
|
||||||
pub fn new<P: IsA<gtk::Window>>(
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
parent: &P,
|
|
||||||
work: Option<Work>,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_editor.ui");
|
||||||
|
|
||||||
get_widget!(builder, gtk::Stack, widget);
|
get_widget!(builder, gtk::Stack, widget);
|
||||||
get_widget!(builder, gtk::Button, cancel_button);
|
get_widget!(builder, gtk::Button, back_button);
|
||||||
get_widget!(builder, gtk::Button, save_button);
|
get_widget!(builder, gtk::Button, save_button);
|
||||||
get_widget!(builder, gtk::InfoBar, info_bar);
|
get_widget!(builder, gtk::InfoBar, info_bar);
|
||||||
get_widget!(builder, gtk::Entry, title_entry);
|
get_widget!(builder, gtk::Entry, title_entry);
|
||||||
|
|
@ -102,7 +96,6 @@ impl WorkEditor {
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
widget,
|
widget,
|
||||||
backend,
|
backend,
|
||||||
parent: parent.clone().upcast(),
|
|
||||||
save_button,
|
save_button,
|
||||||
id,
|
id,
|
||||||
info_bar,
|
info_bar,
|
||||||
|
|
@ -114,15 +107,16 @@ impl WorkEditor {
|
||||||
composer: RefCell::new(composer),
|
composer: RefCell::new(composer),
|
||||||
instruments: RefCell::new(instruments),
|
instruments: RefCell::new(instruments),
|
||||||
structure: RefCell::new(structure),
|
structure: RefCell::new(structure),
|
||||||
cancel_cb: RefCell::new(None),
|
|
||||||
saved_cb: RefCell::new(None),
|
saved_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
cancel_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
if let Some(cb) = &*this.cancel_cb.borrow() {
|
let navigator = this.navigator.borrow().clone();
|
||||||
cb();
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -134,7 +128,10 @@ impl WorkEditor {
|
||||||
clone.widget.set_visible_child_name("loading");
|
clone.widget.set_visible_child_name("loading");
|
||||||
match clone.clone().save().await {
|
match clone.clone().save().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// We already called the callback.
|
let navigator = clone.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
clone.info_bar.set_revealed(true);
|
clone.info_bar.set_revealed(true);
|
||||||
|
|
@ -146,14 +143,18 @@ impl WorkEditor {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
composer_button.connect_clicked(clone!(@strong this => move |_| {
|
composer_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let dialog = PersonSelector::new(this.backend.clone(), &this.parent);
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let selector = PersonSelector::new(this.backend.clone());
|
||||||
|
|
||||||
dialog.set_selected_cb(clone!(@strong this => move |person| {
|
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |person| {
|
||||||
this.show_composer(&person);
|
this.show_composer(person);
|
||||||
this.composer.replace(Some(person));
|
this.composer.replace(Some(person.clone()));
|
||||||
}));
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
dialog.show();
|
navigator.push(selector);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.instrument_list.set_make_widget(|instrument| {
|
this.instrument_list.set_make_widget(|instrument| {
|
||||||
|
|
@ -168,22 +169,27 @@ impl WorkEditor {
|
||||||
});
|
});
|
||||||
|
|
||||||
add_instrument_button.connect_clicked(clone!(@strong this => move |_| {
|
add_instrument_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let dialog = InstrumentSelector::new(this.backend.clone(), &this.parent);
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let selector = InstrumentSelector::new(this.backend.clone());
|
||||||
|
|
||||||
dialog.set_selected_cb(clone!(@strong this => move |instrument| {
|
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |instrument| {
|
||||||
let mut instruments = this.instruments.borrow_mut();
|
let mut instruments = this.instruments.borrow_mut();
|
||||||
|
|
||||||
let index = match this.instrument_list.get_selected_index() {
|
let index = match this.instrument_list.get_selected_index() {
|
||||||
Some(index) => index + 1,
|
Some(index) => index + 1,
|
||||||
None => instruments.len(),
|
None => instruments.len(),
|
||||||
};
|
};
|
||||||
|
|
||||||
instruments.insert(index, instrument);
|
instruments.insert(index, instrument.clone());
|
||||||
this.instrument_list.show_items(instruments.clone());
|
this.instrument_list.show_items(instruments.clone());
|
||||||
this.instrument_list.select_index(index);
|
this.instrument_list.select_index(index);
|
||||||
}));
|
|
||||||
|
|
||||||
dialog.show();
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
|
navigator.push(selector);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
remove_instrument_button.connect_clicked(clone!(@strong this => move |_| {
|
remove_instrument_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
|
@ -221,73 +227,84 @@ impl WorkEditor {
|
||||||
});
|
});
|
||||||
|
|
||||||
add_part_button.connect_clicked(clone!(@strong this => move |_| {
|
add_part_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let editor = PartEditor::new(this.backend.clone(), &this.parent, None);
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let editor = WorkPartEditor::new(this.backend.clone(), None);
|
||||||
|
|
||||||
editor.set_ready_cb(clone!(@strong this => move |part| {
|
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |part| {
|
||||||
let mut structure = this.structure.borrow_mut();
|
let mut structure = this.structure.borrow_mut();
|
||||||
|
|
||||||
let index = match this.part_list.get_selected_index() {
|
let index = match this.part_list.get_selected_index() {
|
||||||
Some(index) => index + 1,
|
Some(index) => index + 1,
|
||||||
None => structure.len(),
|
None => structure.len(),
|
||||||
};
|
};
|
||||||
|
|
||||||
structure.insert(index, PartOrSection::Part(part));
|
structure.insert(index, PartOrSection::Part(part));
|
||||||
this.part_list.show_items(structure.clone());
|
this.part_list.show_items(structure.clone());
|
||||||
this.part_list.select_index(index);
|
this.part_list.select_index(index);
|
||||||
}));
|
|
||||||
|
|
||||||
editor.show();
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
|
navigator.push(editor);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
add_section_button.connect_clicked(clone!(@strong this => move |_| {
|
add_section_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let editor = SectionEditor::new(&this.parent, None);
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let editor = WorkSectionEditor::new(None);
|
||||||
|
|
||||||
editor.set_ready_cb(clone!(@strong this => move |section| {
|
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |section| {
|
||||||
let mut structure = this.structure.borrow_mut();
|
let mut structure = this.structure.borrow_mut();
|
||||||
|
|
||||||
let index = match this.part_list.get_selected_index() {
|
let index = match this.part_list.get_selected_index() {
|
||||||
Some(index) => index + 1,
|
Some(index) => index + 1,
|
||||||
None => structure.len(),
|
None => structure.len(),
|
||||||
};
|
};
|
||||||
|
|
||||||
structure.insert(index, PartOrSection::Section(section));
|
structure.insert(index, PartOrSection::Section(section));
|
||||||
this.part_list.show_items(structure.clone());
|
this.part_list.show_items(structure.clone());
|
||||||
this.part_list.select_index(index);
|
this.part_list.select_index(index);
|
||||||
}));
|
|
||||||
|
|
||||||
editor.show();
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
|
navigator.push(editor);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
edit_part_button.connect_clicked(clone!(@strong this => move |_| {
|
edit_part_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
if let Some(index) = this.part_list.get_selected_index() {
|
let navigator = this.navigator.borrow().clone();
|
||||||
match this.structure.borrow()[index].clone() {
|
if let Some(navigator) = navigator {
|
||||||
PartOrSection::Part(part) => {
|
if let Some(index) = this.part_list.get_selected_index() {
|
||||||
let editor = PartEditor::new(
|
match this.structure.borrow()[index].clone() {
|
||||||
this.backend.clone(),
|
PartOrSection::Part(part) => {
|
||||||
&this.parent,
|
let editor = WorkPartEditor::new(this.backend.clone(), Some(part));
|
||||||
Some(part),
|
|
||||||
);
|
|
||||||
|
|
||||||
editor.set_ready_cb(clone!(@strong this => move |part| {
|
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |part| {
|
||||||
let mut structure = this.structure.borrow_mut();
|
let mut structure = this.structure.borrow_mut();
|
||||||
structure[index] = PartOrSection::Part(part);
|
structure[index] = PartOrSection::Part(part);
|
||||||
this.part_list.show_items(structure.clone());
|
this.part_list.show_items(structure.clone());
|
||||||
this.part_list.select_index(index);
|
this.part_list.select_index(index);
|
||||||
}));
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
editor.show();
|
navigator.push(editor);
|
||||||
}
|
}
|
||||||
PartOrSection::Section(section) => {
|
PartOrSection::Section(section) => {
|
||||||
let editor = SectionEditor::new(&this.parent, Some(section));
|
let editor = WorkSectionEditor::new(Some(section));
|
||||||
|
|
||||||
editor.set_ready_cb(clone!(@strong this => move |section| {
|
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |section| {
|
||||||
let mut structure = this.structure.borrow_mut();
|
let mut structure = this.structure.borrow_mut();
|
||||||
structure[index] = PartOrSection::Section(section);
|
structure[index] = PartOrSection::Section(section);
|
||||||
this.part_list.show_items(structure.clone());
|
this.part_list.show_items(structure.clone());
|
||||||
this.part_list.select_index(index);
|
this.part_list.select_index(index);
|
||||||
}));
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
editor.show();
|
navigator.push(editor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -337,11 +354,6 @@ impl WorkEditor {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The closure to call when the editor is canceled.
|
|
||||||
pub fn set_cancel_cb<F: Fn() -> () + 'static>(&self, cb: F) {
|
|
||||||
self.cancel_cb.replace(Some(Box::new(cb)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The closure to call when a work was created.
|
/// The closure to call when a work was created.
|
||||||
pub fn set_saved_cb<F: Fn(Work) -> () + 'static>(&self, cb: F) {
|
pub fn set_saved_cb<F: Fn(Work) -> () + 'static>(&self, cb: F) {
|
||||||
self.saved_cb.replace(Some(Box::new(cb)));
|
self.saved_cb.replace(Some(Box::new(cb)));
|
||||||
|
|
@ -404,3 +416,17 @@ impl WorkEditor {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for WorkEditor {
|
||||||
|
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,6 +1,7 @@
|
||||||
use crate::backend::*;
|
use crate::backend::Backend;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::dialogs::*;
|
use crate::selectors::PersonSelector;
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
@ -9,37 +10,32 @@ use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// A dialog for creating or editing a work part.
|
/// A dialog for creating or editing a work part.
|
||||||
pub struct PartEditor {
|
pub struct WorkPartEditor {
|
||||||
backend: Rc<Backend>,
|
backend: Rc<Backend>,
|
||||||
window: libhandy::Window,
|
widget: gtk::Box,
|
||||||
title_entry: gtk::Entry,
|
title_entry: gtk::Entry,
|
||||||
composer_label: gtk::Label,
|
composer_label: gtk::Label,
|
||||||
reset_composer_button: gtk::Button,
|
reset_composer_button: gtk::Button,
|
||||||
composer: RefCell<Option<Person>>,
|
composer: RefCell<Option<Person>>,
|
||||||
ready_cb: RefCell<Option<Box<dyn Fn(WorkPart) -> ()>>>,
|
ready_cb: RefCell<Option<Box<dyn Fn(WorkPart) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartEditor {
|
impl WorkPartEditor {
|
||||||
/// Create a new part editor and optionally initialize it.
|
/// Create a new part editor and optionally initialize it.
|
||||||
pub fn new<P: IsA<gtk::Window>>(
|
pub fn new(backend: Rc<Backend>, part: Option<WorkPart>) -> Rc<Self> {
|
||||||
backend: Rc<Backend>,
|
|
||||||
parent: &P,
|
|
||||||
part: Option<WorkPart>,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/part_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_part_editor.ui");
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
get_widget!(builder, gtk::Box, widget);
|
||||||
get_widget!(builder, gtk::Button, cancel_button);
|
get_widget!(builder, gtk::Button, back_button);
|
||||||
get_widget!(builder, gtk::Button, save_button);
|
get_widget!(builder, gtk::Button, save_button);
|
||||||
get_widget!(builder, gtk::Entry, title_entry);
|
get_widget!(builder, gtk::Entry, title_entry);
|
||||||
get_widget!(builder, gtk::Button, composer_button);
|
get_widget!(builder, gtk::Button, composer_button);
|
||||||
get_widget!(builder, gtk::Label, composer_label);
|
get_widget!(builder, gtk::Label, composer_label);
|
||||||
get_widget!(builder, gtk::Button, reset_composer_button);
|
get_widget!(builder, gtk::Button, reset_composer_button);
|
||||||
|
|
||||||
window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
let composer = match part {
|
let composer = match part {
|
||||||
Some(part) => {
|
Some(part) => {
|
||||||
title_entry.set_text(&part.title);
|
title_entry.set_text(&part.title);
|
||||||
|
|
@ -50,18 +46,22 @@ impl PartEditor {
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
backend,
|
backend,
|
||||||
window,
|
widget,
|
||||||
title_entry,
|
title_entry,
|
||||||
composer_label,
|
composer_label,
|
||||||
reset_composer_button,
|
reset_composer_button,
|
||||||
composer: RefCell::new(composer),
|
composer: RefCell::new(composer),
|
||||||
ready_cb: RefCell::new(None),
|
ready_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
cancel_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
this.window.close();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
save_button.connect_clicked(clone!(@strong this => move |_| {
|
save_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
|
@ -72,18 +72,26 @@ impl PartEditor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.window.close();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
composer_button.connect_clicked(clone!(@strong this => move |_| {
|
composer_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let dialog = PersonSelector::new(this.backend.clone(), &this.window);
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let selector = PersonSelector::new(this.backend.clone());
|
||||||
|
|
||||||
dialog.set_selected_cb(clone!(@strong this => move |person| {
|
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |person| {
|
||||||
this.show_composer(Some(&person));
|
this.show_composer(Some(person));
|
||||||
this.composer.replace(Some(person));
|
this.composer.replace(Some(person.clone()));
|
||||||
}));
|
navigator.clone().pop();
|
||||||
|
}));
|
||||||
|
|
||||||
|
navigator.push(selector);
|
||||||
|
}
|
||||||
|
|
||||||
dialog.show();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.reset_composer_button
|
this.reset_composer_button
|
||||||
|
|
@ -106,11 +114,6 @@ impl PartEditor {
|
||||||
self.ready_cb.replace(Some(Box::new(cb)));
|
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.
|
/// Update the UI according to person.
|
||||||
fn show_composer(&self, person: Option<&Person>) {
|
fn show_composer(&self, person: Option<&Person>) {
|
||||||
if let Some(person) = person {
|
if let Some(person) = person {
|
||||||
|
|
@ -122,3 +125,17 @@ impl PartEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for WorkPartEditor {
|
||||||
|
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,4 +1,5 @@
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk_macros::get_widget;
|
use gtk_macros::get_widget;
|
||||||
|
|
@ -6,40 +7,43 @@ use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// A dialog for creating or editing a work section.
|
/// A dialog for creating or editing a work section.
|
||||||
pub struct SectionEditor {
|
pub struct WorkSectionEditor {
|
||||||
window: libhandy::Window,
|
widget: gtk::Box,
|
||||||
title_entry: gtk::Entry,
|
title_entry: gtk::Entry,
|
||||||
ready_cb: RefCell<Option<Box<dyn Fn(WorkSection) -> ()>>>,
|
ready_cb: RefCell<Option<Box<dyn Fn(WorkSection) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SectionEditor {
|
impl WorkSectionEditor {
|
||||||
/// Create a new section editor and optionally initialize it.
|
/// Create a new section editor and optionally initialize it.
|
||||||
pub fn new<P: IsA<gtk::Window>>(parent: &P, section: Option<WorkSection>) -> Rc<Self> {
|
pub fn new(section: Option<WorkSection>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/section_editor.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_section_editor.ui");
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
get_widget!(builder, gtk::Box, widget);
|
||||||
get_widget!(builder, gtk::Button, cancel_button);
|
get_widget!(builder, gtk::Button, back_button);
|
||||||
get_widget!(builder, gtk::Button, save_button);
|
get_widget!(builder, gtk::Button, save_button);
|
||||||
get_widget!(builder, gtk::Entry, title_entry);
|
get_widget!(builder, gtk::Entry, title_entry);
|
||||||
|
|
||||||
window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
if let Some(section) = section {
|
if let Some(section) = section {
|
||||||
title_entry.set_text(§ion.title);
|
title_entry.set_text(§ion.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
window,
|
widget,
|
||||||
title_entry,
|
title_entry,
|
||||||
ready_cb: RefCell::new(None),
|
ready_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect signals and callbacks
|
// Connect signals and callbacks
|
||||||
|
|
||||||
cancel_button.connect_clicked(clone!(@strong this => move |_| {
|
back_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
this.window.close();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
save_button.connect_clicked(clone!(@strong this => move |_| {
|
save_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
|
@ -50,7 +54,10 @@ impl SectionEditor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.window.close();
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this
|
this
|
||||||
|
|
@ -62,9 +69,18 @@ impl SectionEditor {
|
||||||
pub fn set_ready_cb<F: Fn(WorkSection) -> () + 'static>(&self, cb: F) {
|
pub fn set_ready_cb<F: Fn(WorkSection) -> () + 'static>(&self, cb: F) {
|
||||||
self.ready_cb.replace(Some(Box::new(cb)));
|
self.ready_cb.replace(Some(Box::new(cb)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Show the section editor.
|
impl NavigatorScreen for WorkSectionEditor {
|
||||||
pub fn show(&self) {
|
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
||||||
self.window.show();
|
self.navigator.replace(Some(navigator));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
|
self.widget.clone().upcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detach_navigator(&self) {
|
||||||
|
self.navigator.replace(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11,12 +11,14 @@ use glib::clone;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
mod config;
|
|
||||||
mod backend;
|
mod backend;
|
||||||
|
mod config;
|
||||||
mod database;
|
mod database;
|
||||||
mod dialogs;
|
mod dialogs;
|
||||||
|
mod editors;
|
||||||
mod player;
|
mod player;
|
||||||
mod screens;
|
mod screens;
|
||||||
|
mod selectors;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
|
|
||||||
mod window;
|
mod window;
|
||||||
|
|
@ -33,11 +35,8 @@ fn main() {
|
||||||
libhandy::init();
|
libhandy::init();
|
||||||
resources::init().expect("Failed to initialize resources!");
|
resources::init().expect("Failed to initialize resources!");
|
||||||
|
|
||||||
let app = gtk::Application::new(
|
let app = gtk::Application::new(Some("de.johrpan.musicus"), gio::ApplicationFlags::empty())
|
||||||
Some("de.johrpan.musicus"),
|
.expect("Failed to initialize GTK application!");
|
||||||
gio::ApplicationFlags::empty(),
|
|
||||||
)
|
|
||||||
.expect("Failed to initialize GTK application!");
|
|
||||||
|
|
||||||
let window: RefCell<Option<Rc<Window>>> = RefCell::new(None);
|
let window: RefCell<Option<Rc<Window>>> = RefCell::new(None);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,43 +52,38 @@ sources = files(
|
||||||
'database/tracks.rs',
|
'database/tracks.rs',
|
||||||
'database/works.rs',
|
'database/works.rs',
|
||||||
'dialogs/about.rs',
|
'dialogs/about.rs',
|
||||||
'dialogs/ensemble_editor.rs',
|
|
||||||
'dialogs/ensemble_selector.rs',
|
|
||||||
'dialogs/instrument_editor.rs',
|
|
||||||
'dialogs/instrument_selector.rs',
|
|
||||||
'dialogs/login_dialog.rs',
|
'dialogs/login_dialog.rs',
|
||||||
'dialogs/mod.rs',
|
'dialogs/mod.rs',
|
||||||
'dialogs/person_editor.rs',
|
|
||||||
'dialogs/person_selector.rs',
|
|
||||||
'dialogs/preferences.rs',
|
'dialogs/preferences.rs',
|
||||||
'dialogs/server_dialog.rs',
|
'dialogs/server_dialog.rs',
|
||||||
'dialogs/recording/mod.rs',
|
'editors/ensemble.rs',
|
||||||
'dialogs/recording/performance_editor.rs',
|
'editors/instrument.rs',
|
||||||
'dialogs/recording/recording_dialog.rs',
|
'editors/mod.rs',
|
||||||
'dialogs/recording/recording_editor_dialog.rs',
|
'editors/performance.rs',
|
||||||
'dialogs/recording/recording_editor.rs',
|
'editors/person.rs',
|
||||||
'dialogs/recording/recording_selector_person_screen.rs',
|
'editors/recording.rs',
|
||||||
'dialogs/recording/recording_selector.rs',
|
'editors/track.rs',
|
||||||
'dialogs/recording/recording_selector_work_screen.rs',
|
'editors/tracks.rs',
|
||||||
'dialogs/track_editor.rs',
|
'editors/work.rs',
|
||||||
'dialogs/tracks_editor.rs',
|
'editors/work_part.rs',
|
||||||
'dialogs/work/mod.rs',
|
'editors/work_section.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/ensemble_screen.rs',
|
||||||
'screens/mod.rs',
|
'screens/mod.rs',
|
||||||
'screens/person_screen.rs',
|
'screens/person_screen.rs',
|
||||||
'screens/player_screen.rs',
|
'screens/player_screen.rs',
|
||||||
'screens/recording_screen.rs',
|
'screens/recording_screen.rs',
|
||||||
'screens/work_screen.rs',
|
'screens/work_screen.rs',
|
||||||
|
'selectors/ensemble.rs',
|
||||||
|
'selectors/instrument.rs',
|
||||||
|
'selectors/mod.rs',
|
||||||
|
'selectors/person.rs',
|
||||||
|
'selectors/recording.rs',
|
||||||
|
'selectors/selector.rs',
|
||||||
|
'selectors/work.rs',
|
||||||
'widgets/list.rs',
|
'widgets/list.rs',
|
||||||
'widgets/mod.rs',
|
'widgets/mod.rs',
|
||||||
'widgets/navigator.rs',
|
'widgets/navigator.rs',
|
||||||
|
'widgets/navigator_window.rs',
|
||||||
'widgets/player_bar.rs',
|
'widgets/player_bar.rs',
|
||||||
'widgets/poe_list.rs',
|
'widgets/poe_list.rs',
|
||||||
'widgets/selector_row.rs',
|
'widgets/selector_row.rs',
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::backend::*;
|
use crate::backend::*;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::dialogs::EnsembleEditor;
|
use crate::editors::EnsembleEditor;
|
||||||
use crate::widgets::*;
|
use crate::widgets::{List, Navigator, NavigatorScreen, NavigatorWindow};
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use gio::prelude::*;
|
use gio::prelude::*;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -109,7 +109,9 @@ impl EnsembleScreen {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
edit_action.connect_activate(clone!(@strong result => move |_, _| {
|
edit_action.connect_activate(clone!(@strong result => move |_, _| {
|
||||||
EnsembleEditor::new(result.backend.clone(), &result.window, Some(result.ensemble.clone())).show();
|
let editor = EnsembleEditor::new(result.backend.clone(), Some(result.ensemble.clone()));
|
||||||
|
let window = NavigatorWindow::new(editor);
|
||||||
|
window.show();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
delete_action.connect_activate(clone!(@strong result => move |_, _| {
|
delete_action.connect_activate(clone!(@strong result => move |_, _| {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::backend::*;
|
use crate::backend::*;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::dialogs::PersonEditor;
|
use crate::editors::PersonEditor;
|
||||||
use crate::widgets::*;
|
use crate::widgets::{List, Navigator, NavigatorScreen, NavigatorWindow};
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use gio::prelude::*;
|
use gio::prelude::*;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -145,7 +145,9 @@ impl PersonScreen {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
edit_action.connect_activate(clone!(@strong result => move |_, _| {
|
edit_action.connect_activate(clone!(@strong result => move |_, _| {
|
||||||
PersonEditor::new(result.backend.clone(), &result.window, Some(result.person.clone())).show();
|
let editor = PersonEditor::new(result.backend.clone(), Some(result.person.clone()));
|
||||||
|
let window = NavigatorWindow::new(editor);
|
||||||
|
window.show();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
delete_action.connect_activate(clone!(@strong result => move |_, _| {
|
delete_action.connect_activate(clone!(@strong result => move |_, _| {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::backend::*;
|
use crate::backend::*;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::dialogs::{RecordingEditorDialog, TracksEditor};
|
use crate::editors::{RecordingEditor, TracksEditor};
|
||||||
use crate::player::*;
|
use crate::player::*;
|
||||||
use crate::widgets::*;
|
use crate::widgets::{List, Navigator, NavigatorScreen, NavigatorWindow};
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use gio::prelude::*;
|
use gio::prelude::*;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -111,7 +111,9 @@ impl RecordingScreen {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
edit_action.connect_activate(clone!(@strong result => move |_, _| {
|
edit_action.connect_activate(clone!(@strong result => move |_, _| {
|
||||||
RecordingEditorDialog::new(result.backend.clone(), &result.window, Some(result.recording.clone())).show();
|
let editor = RecordingEditor::new(result.backend.clone(), Some(result.recording.clone()));
|
||||||
|
let window = NavigatorWindow::new(editor);
|
||||||
|
window.show();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
delete_action.connect_activate(clone!(@strong result => move |_, _| {
|
delete_action.connect_activate(clone!(@strong result => move |_, _| {
|
||||||
|
|
@ -124,7 +126,9 @@ impl RecordingScreen {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
edit_tracks_action.connect_activate(clone!(@strong result => move |_, _| {
|
edit_tracks_action.connect_activate(clone!(@strong result => move |_, _| {
|
||||||
TracksEditor::new(result.backend.clone(), &result.window, Some(result.recording.clone()), result.tracks.borrow().clone()).show();
|
let editor = TracksEditor::new(result.backend.clone(), Some(result.recording.clone()), result.tracks.borrow().clone());
|
||||||
|
let window = NavigatorWindow::new(editor);
|
||||||
|
window.show();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
delete_tracks_action.connect_activate(clone!(@strong result => move |_, _| {
|
delete_tracks_action.connect_activate(clone!(@strong result => move |_, _| {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::backend::*;
|
use crate::backend::*;
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::dialogs::WorkEditorDialog;
|
use crate::editors::WorkEditor;
|
||||||
use crate::widgets::*;
|
use crate::widgets::{List, Navigator, NavigatorScreen, NavigatorWindow};
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use gio::prelude::*;
|
use gio::prelude::*;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
|
|
@ -108,7 +108,9 @@ impl WorkScreen {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
edit_action.connect_activate(clone!(@strong result => move |_, _| {
|
edit_action.connect_activate(clone!(@strong result => move |_, _| {
|
||||||
WorkEditorDialog::new(result.backend.clone(), &result.window, Some(result.work.clone())).show();
|
let editor = WorkEditor::new(result.backend.clone(), Some(result.work.clone()));
|
||||||
|
let window = NavigatorWindow::new(editor);
|
||||||
|
window.show();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
delete_action.connect_activate(clone!(@strong result => move |_, _| {
|
delete_action.connect_activate(clone!(@strong result => move |_, _| {
|
||||||
|
|
|
||||||
111
musicus/src/selectors/ensemble.rs
Normal file
111
musicus/src/selectors/ensemble.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
use super::selector::Selector;
|
||||||
|
use crate::backend::Backend;
|
||||||
|
use crate::database::Ensemble;
|
||||||
|
use crate::editors::EnsembleEditor;
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
|
use gettextrs::gettext;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A screen for selecting a ensemble.
|
||||||
|
pub struct EnsembleSelector {
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
selector: Rc<Selector<Ensemble>>,
|
||||||
|
selected_cb: RefCell<Option<Box<dyn Fn(&Ensemble) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnsembleSelector {
|
||||||
|
/// Create a new ensemble selector.
|
||||||
|
pub fn new(backend: Rc<Backend>) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let selector = Selector::<Ensemble>::new();
|
||||||
|
selector.set_title(&gettext("Select ensemble"));
|
||||||
|
|
||||||
|
let this = Rc::new(Self {
|
||||||
|
backend,
|
||||||
|
selector,
|
||||||
|
selected_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
this.selector.set_back_cb(clone!(@strong this => move || {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector.set_add_cb(clone!(@strong this => move || {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let editor = EnsembleEditor::new(this.backend.clone(), None);
|
||||||
|
editor
|
||||||
|
.set_saved_cb(clone!(@strong this => move |ensemble| this.select(&ensemble)));
|
||||||
|
navigator.push(editor);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_load_online(clone!(@strong this => move || {
|
||||||
|
let clone = this.clone();
|
||||||
|
async move { clone.backend.get_ensembles().await }
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_load_local(clone!(@strong this => move || {
|
||||||
|
let clone = this.clone();
|
||||||
|
async move { clone.backend.db().get_ensembles().await.unwrap() }
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector.set_make_widget(|ensemble| {
|
||||||
|
let label = gtk::Label::new(Some(&ensemble.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);
|
||||||
|
label.upcast()
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_filter(|search, ensemble| ensemble.name.to_lowercase().contains(search));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_selected_cb(clone!(@strong this => move |ensemble| this.select(ensemble)));
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called when an item is selected.
|
||||||
|
pub fn set_selected_cb<F: Fn(&Ensemble) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.selected_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Select a ensemble.
|
||||||
|
fn select(&self, ensemble: &Ensemble) {
|
||||||
|
if let Some(cb) = &*self.selected_cb.borrow() {
|
||||||
|
cb(&ensemble);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for EnsembleSelector {
|
||||||
|
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
||||||
|
self.navigator.replace(Some(navigator));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
|
self.selector.widget.clone().upcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detach_navigator(&self) {
|
||||||
|
self.navigator.replace(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
110
musicus/src/selectors/instrument.rs
Normal file
110
musicus/src/selectors/instrument.rs
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
use super::selector::Selector;
|
||||||
|
use crate::backend::Backend;
|
||||||
|
use crate::database::Instrument;
|
||||||
|
use crate::editors::InstrumentEditor;
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
|
use gettextrs::gettext;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A screen for selecting a instrument.
|
||||||
|
pub struct InstrumentSelector {
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
selector: Rc<Selector<Instrument>>,
|
||||||
|
selected_cb: RefCell<Option<Box<dyn Fn(&Instrument) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstrumentSelector {
|
||||||
|
/// Create a new instrument selector.
|
||||||
|
pub fn new(backend: Rc<Backend>) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let selector = Selector::<Instrument>::new();
|
||||||
|
selector.set_title(&gettext("Select instrument"));
|
||||||
|
|
||||||
|
let this = Rc::new(Self {
|
||||||
|
backend,
|
||||||
|
selector,
|
||||||
|
selected_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
this.selector.set_back_cb(clone!(@strong this => move || {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector.set_add_cb(clone!(@strong this => move || {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let editor = InstrumentEditor::new(this.backend.clone(), None);
|
||||||
|
editor
|
||||||
|
.set_saved_cb(clone!(@strong this => move |instrument| this.select(&instrument)));
|
||||||
|
navigator.push(editor);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_load_online(clone!(@strong this => move || {
|
||||||
|
let clone = this.clone();
|
||||||
|
async move { clone.backend.get_instruments().await }
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_load_local(clone!(@strong this => move || {
|
||||||
|
let clone = this.clone();
|
||||||
|
async move { clone.backend.db().get_instruments().await.unwrap() }
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector.set_make_widget(|instrument| {
|
||||||
|
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);
|
||||||
|
label.upcast()
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_filter(|search, instrument| instrument.name.to_lowercase().contains(search));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_selected_cb(clone!(@strong this => move |instrument| this.select(instrument)));
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called when an item is selected.
|
||||||
|
pub fn set_selected_cb<F: Fn(&Instrument) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.selected_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Select a instrument.
|
||||||
|
fn select(&self, instrument: &Instrument) {
|
||||||
|
if let Some(cb) = &*self.selected_cb.borrow() {
|
||||||
|
cb(&instrument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for InstrumentSelector {
|
||||||
|
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
||||||
|
self.navigator.replace(Some(navigator));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
|
self.selector.widget.clone().upcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detach_navigator(&self) {
|
||||||
|
self.navigator.replace(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
musicus/src/selectors/mod.rs
Normal file
16
musicus/src/selectors/mod.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
pub mod ensemble;
|
||||||
|
pub use ensemble::*;
|
||||||
|
|
||||||
|
pub mod instrument;
|
||||||
|
pub use instrument::*;
|
||||||
|
|
||||||
|
pub mod person;
|
||||||
|
pub use person::*;
|
||||||
|
|
||||||
|
pub mod recording;
|
||||||
|
pub use recording::*;
|
||||||
|
|
||||||
|
pub mod work;
|
||||||
|
pub use work::*;
|
||||||
|
|
||||||
|
mod selector;
|
||||||
110
musicus/src/selectors/person.rs
Normal file
110
musicus/src/selectors/person.rs
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
use super::selector::Selector;
|
||||||
|
use crate::backend::Backend;
|
||||||
|
use crate::database::Person;
|
||||||
|
use crate::editors::PersonEditor;
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
|
use gettextrs::gettext;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A screen for selecting a person.
|
||||||
|
pub struct PersonSelector {
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
selector: Rc<Selector<Person>>,
|
||||||
|
selected_cb: RefCell<Option<Box<dyn Fn(&Person) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PersonSelector {
|
||||||
|
/// Create a new person selector.
|
||||||
|
pub fn new(backend: Rc<Backend>) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let selector = Selector::<Person>::new();
|
||||||
|
selector.set_title(&gettext("Select person"));
|
||||||
|
|
||||||
|
let this = Rc::new(Self {
|
||||||
|
backend,
|
||||||
|
selector,
|
||||||
|
selected_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
this.selector.set_back_cb(clone!(@strong this => move || {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector.set_add_cb(clone!(@strong this => move || {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let editor = PersonEditor::new(this.backend.clone(), None);
|
||||||
|
editor
|
||||||
|
.set_saved_cb(clone!(@strong this => move |person| this.select(&person)));
|
||||||
|
navigator.push(editor);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_load_online(clone!(@strong this => move || {
|
||||||
|
let clone = this.clone();
|
||||||
|
async move { clone.backend.get_persons().await }
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_load_local(clone!(@strong this => move || {
|
||||||
|
let clone = this.clone();
|
||||||
|
async move { clone.backend.db().get_persons().await.unwrap() }
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector.set_make_widget(|person| {
|
||||||
|
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);
|
||||||
|
label.upcast()
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_filter(|search, person| person.name_fl().to_lowercase().contains(search));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_selected_cb(clone!(@strong this => move |person| this.select(person)));
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called when an item is selected.
|
||||||
|
pub fn set_selected_cb<F: Fn(&Person) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.selected_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Select a person.
|
||||||
|
fn select(&self, person: &Person) {
|
||||||
|
if let Some(cb) = &*self.selected_cb.borrow() {
|
||||||
|
cb(&person);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for PersonSelector {
|
||||||
|
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
||||||
|
self.navigator.replace(Some(navigator));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
|
self.selector.widget.clone().upcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detach_navigator(&self) {
|
||||||
|
self.navigator.replace(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
114
musicus/src/selectors/recording.rs
Normal file
114
musicus/src/selectors/recording.rs
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
use super::selector::Selector;
|
||||||
|
use crate::backend::Backend;
|
||||||
|
use crate::database::{Recording, Work};
|
||||||
|
use crate::editors::RecordingEditor;
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
|
use gettextrs::gettext;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A screen for selecting a recording.
|
||||||
|
pub struct RecordingSelector {
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
work: Work,
|
||||||
|
selector: Rc<Selector<Recording>>,
|
||||||
|
selected_cb: RefCell<Option<Box<dyn Fn(&Recording) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecordingSelector {
|
||||||
|
/// Create a new recording selector for recordings of a specific work.
|
||||||
|
pub fn new(backend: Rc<Backend>, work: Work) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let selector = Selector::<Recording>::new();
|
||||||
|
selector.set_title(&gettext("Select recording"));
|
||||||
|
selector.set_subtitle(&work.get_title());
|
||||||
|
|
||||||
|
let this = Rc::new(Self {
|
||||||
|
backend,
|
||||||
|
work,
|
||||||
|
selector,
|
||||||
|
selected_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
this.selector.set_back_cb(clone!(@strong this => move || {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector.set_add_cb(clone!(@strong this => move || {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let editor = RecordingEditor::new(this.backend.clone(), None);
|
||||||
|
editor
|
||||||
|
.set_selected_cb(clone!(@strong this => move |recording| this.select(&recording)));
|
||||||
|
navigator.push(editor);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_load_online(clone!(@strong this => move || {
|
||||||
|
let clone = this.clone();
|
||||||
|
async move { clone.backend.get_recordings_for_work(&clone.work.id).await }
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_load_local(clone!(@strong this => move || {
|
||||||
|
let clone = this.clone();
|
||||||
|
async move { clone.backend.db().get_recordings_for_work(&clone.work.id).await.unwrap() }
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector.set_make_widget(|recording| {
|
||||||
|
let label = gtk::Label::new(Some(&recording.get_performers()));
|
||||||
|
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.selector.set_filter(|search, recording| {
|
||||||
|
recording.get_performers().to_lowercase().contains(search)
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_selected_cb(clone!(@strong this => move |recording| this.select(recording)));
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called when an item is selected.
|
||||||
|
pub fn set_selected_cb<F: Fn(&Recording) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.selected_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Select a recording.
|
||||||
|
fn select(&self, recording: &Recording) {
|
||||||
|
if let Some(cb) = &*self.selected_cb.borrow() {
|
||||||
|
cb(&recording);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for RecordingSelector {
|
||||||
|
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
||||||
|
self.navigator.replace(Some(navigator));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
|
self.selector.widget.clone().upcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detach_navigator(&self) {
|
||||||
|
self.navigator.replace(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
201
musicus/src/selectors/selector.rs
Normal file
201
musicus/src/selectors/selector.rs
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
use crate::widgets::List;
|
||||||
|
use anyhow::Result;
|
||||||
|
use gettextrs::gettext;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use gtk_macros::get_widget;
|
||||||
|
use libhandy::HeaderBarExt;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A screen that presents a list of items. It allows to switch between the server and the local
|
||||||
|
/// database and to search within the list.
|
||||||
|
pub struct Selector<T: 'static> {
|
||||||
|
pub widget: gtk::Box,
|
||||||
|
header: libhandy::HeaderBar,
|
||||||
|
server_check_button: gtk::CheckButton,
|
||||||
|
stack: gtk::Stack,
|
||||||
|
list: Rc<List<T>>,
|
||||||
|
back_cb: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
||||||
|
add_cb: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
||||||
|
load_online: RefCell<Option<Box<dyn Fn() -> Box<dyn Future<Output = Result<Vec<T>>>>>>>,
|
||||||
|
load_local: RefCell<Option<Box<dyn Fn() -> Box<dyn Future<Output = Vec<T>>>>>>,
|
||||||
|
filter: RefCell<Option<Box<dyn Fn(&str, &T) -> bool>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Selector<T> {
|
||||||
|
/// Create a new selector.
|
||||||
|
pub fn new() -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/selector.ui");
|
||||||
|
|
||||||
|
get_widget!(builder, gtk::Box, widget);
|
||||||
|
get_widget!(builder, libhandy::HeaderBar, header);
|
||||||
|
get_widget!(builder, gtk::Button, back_button);
|
||||||
|
get_widget!(builder, gtk::Button, add_button);
|
||||||
|
get_widget!(builder, gtk::SearchEntry, search_entry);
|
||||||
|
get_widget!(builder, gtk::CheckButton, server_check_button);
|
||||||
|
get_widget!(builder, gtk::Stack, stack);
|
||||||
|
get_widget!(builder, gtk::Frame, frame);
|
||||||
|
get_widget!(builder, gtk::Button, try_again_button);
|
||||||
|
|
||||||
|
let list = List::<T>::new(&gettext("Nothing found."));
|
||||||
|
frame.add(&list.widget);
|
||||||
|
|
||||||
|
let this = Rc::new(Self {
|
||||||
|
widget,
|
||||||
|
header,
|
||||||
|
server_check_button,
|
||||||
|
stack,
|
||||||
|
list,
|
||||||
|
back_cb: RefCell::new(None),
|
||||||
|
add_cb: RefCell::new(None),
|
||||||
|
load_online: RefCell::new(None),
|
||||||
|
load_local: RefCell::new(None),
|
||||||
|
filter: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
back_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
if let Some(cb) = &*this.back_cb.borrow() {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
add_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
if let Some(cb) = &*this.add_cb.borrow() {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
search_entry.connect_search_changed(clone!(@strong this => move |_| {
|
||||||
|
this.list.invalidate_filter();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.server_check_button
|
||||||
|
.connect_toggled(clone!(@strong this => move |_| {
|
||||||
|
if this.server_check_button.get_active() {
|
||||||
|
this.clone().load_online();
|
||||||
|
} else {
|
||||||
|
this.clone().load_local();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.list.set_filter(
|
||||||
|
clone!(@strong this, @strong search_entry => move |item: &T| {
|
||||||
|
match &*this.filter.borrow() {
|
||||||
|
Some(filter) => {
|
||||||
|
let search = search_entry.get_text().to_string().to_lowercase();
|
||||||
|
search.is_empty() || filter(&search, item)
|
||||||
|
}
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
try_again_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
this.clone().load_online();
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
this.clone().load_online();
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the title to be shown in the header.
|
||||||
|
pub fn set_title(&self, title: &str) {
|
||||||
|
self.header.set_title(Some(title));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the subtitle to be shown in the header.
|
||||||
|
pub fn set_subtitle(&self, subtitle: &str) {
|
||||||
|
self.header.set_subtitle(Some(subtitle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called when the user wants to go back.
|
||||||
|
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 when the user wants to add an item.
|
||||||
|
pub fn set_add_cb<F: Fn() -> () + 'static>(&self, cb: F) {
|
||||||
|
self.add_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the async closure to be called to fetch items from the server. If that results in an
|
||||||
|
/// error, an error screen is shown allowing to try again.
|
||||||
|
pub fn set_load_online<F, R>(&self, cb: F)
|
||||||
|
where
|
||||||
|
F: (Fn() -> R) + 'static,
|
||||||
|
R: Future<Output = Result<Vec<T>>> + 'static,
|
||||||
|
{
|
||||||
|
self.load_online
|
||||||
|
.replace(Some(Box::new(move || Box::new(cb()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the async closure to be called to get local items.
|
||||||
|
pub fn set_load_local<F, R>(&self, cb: F)
|
||||||
|
where
|
||||||
|
F: (Fn() -> R) + 'static,
|
||||||
|
R: Future<Output = Vec<T>> + 'static,
|
||||||
|
{
|
||||||
|
self.load_local
|
||||||
|
.replace(Some(Box::new(move || Box::new(cb()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called for creating a new list row.
|
||||||
|
pub fn set_make_widget<F: Fn(&T) -> gtk::Widget + 'static>(&self, make_widget: F) {
|
||||||
|
self.list.set_make_widget(make_widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a closure to call when deciding whether to show an item based on a search string. The
|
||||||
|
/// search string will be converted to lowercase.
|
||||||
|
pub fn set_filter<F: Fn(&str, &T) -> bool + 'static>(&self, filter: F) {
|
||||||
|
self.filter.replace(Some(Box::new(filter)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called when an item is selected.
|
||||||
|
pub fn set_selected_cb<F: Fn(&T) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.list.set_selected(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_online(self: Rc<Self>) {
|
||||||
|
let context = glib::MainContext::default();
|
||||||
|
let clone = self.clone();
|
||||||
|
context.spawn_local(async move {
|
||||||
|
if let Some(cb) = &*self.load_online.borrow() {
|
||||||
|
self.stack.set_visible_child_name("loading");
|
||||||
|
|
||||||
|
match Pin::from(cb()).await {
|
||||||
|
Ok(items) => {
|
||||||
|
clone.list.show_items(items);
|
||||||
|
clone.stack.set_visible_child_name("content");
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
clone.list.show_items(Vec::new());
|
||||||
|
clone.stack.set_visible_child_name("error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_local(self: Rc<Self>) {
|
||||||
|
let context = glib::MainContext::default();
|
||||||
|
let clone = self.clone();
|
||||||
|
context.spawn_local(async move {
|
||||||
|
if let Some(cb) = &*self.load_local.borrow() {
|
||||||
|
self.stack.set_visible_child_name("loading");
|
||||||
|
|
||||||
|
let items = Pin::from(cb()).await;
|
||||||
|
clone.list.show_items(items);
|
||||||
|
clone.stack.set_visible_child_name("content");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
113
musicus/src/selectors/work.rs
Normal file
113
musicus/src/selectors/work.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
use super::selector::Selector;
|
||||||
|
use crate::backend::Backend;
|
||||||
|
use crate::database::{Person, Work};
|
||||||
|
use crate::editors::WorkEditor;
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
|
use gettextrs::gettext;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A screen for selecting a work.
|
||||||
|
pub struct WorkSelector {
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
person: Person,
|
||||||
|
selector: Rc<Selector<Work>>,
|
||||||
|
selected_cb: RefCell<Option<Box<dyn Fn(&Work) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkSelector {
|
||||||
|
/// Create a new work selector for works by a specific composer.
|
||||||
|
pub fn new(backend: Rc<Backend>, person: Person) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let selector = Selector::<Work>::new();
|
||||||
|
selector.set_title(&gettext("Select work"));
|
||||||
|
selector.set_subtitle(&person.name_fl());
|
||||||
|
|
||||||
|
let this = Rc::new(Self {
|
||||||
|
backend,
|
||||||
|
person,
|
||||||
|
selector,
|
||||||
|
selected_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
this.selector.set_back_cb(clone!(@strong this => move || {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector.set_add_cb(clone!(@strong this => move || {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let editor = WorkEditor::new(this.backend.clone(), None);
|
||||||
|
editor
|
||||||
|
.set_saved_cb(clone!(@strong this => move |work| this.select(&work)));
|
||||||
|
navigator.push(editor);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_load_online(clone!(@strong this => move || {
|
||||||
|
let clone = this.clone();
|
||||||
|
async move { clone.backend.get_works(&clone.person.id).await }
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_load_local(clone!(@strong this => move || {
|
||||||
|
let clone = this.clone();
|
||||||
|
async move { clone.backend.db().get_works(&clone.person.id).await.unwrap() }
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector.set_make_widget(|work| {
|
||||||
|
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()
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_filter(|search, work| work.title.to_lowercase().contains(search));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_selected_cb(clone!(@strong this => move |work| this.select(work)));
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called when an item is selected.
|
||||||
|
pub fn set_selected_cb<F: Fn(&Work) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.selected_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Select a work.
|
||||||
|
fn select(&self, work: &Work) {
|
||||||
|
if let Some(cb) = &*self.selected_cb.borrow() {
|
||||||
|
cb(&work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for WorkSelector {
|
||||||
|
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
||||||
|
self.navigator.replace(Some(navigator));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
|
self.selector.widget.clone().upcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detach_navigator(&self) {
|
||||||
|
self.navigator.replace(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,9 @@ pub use list::*;
|
||||||
pub mod navigator;
|
pub mod navigator;
|
||||||
pub use navigator::*;
|
pub use navigator::*;
|
||||||
|
|
||||||
|
pub mod navigator_window;
|
||||||
|
pub use navigator_window::*;
|
||||||
|
|
||||||
pub mod player_bar;
|
pub mod player_bar;
|
||||||
pub use player_bar::*;
|
pub use player_bar::*;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ pub trait NavigatorScreen {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Navigator {
|
pub struct Navigator {
|
||||||
|
pub window: gtk::Window,
|
||||||
pub widget: gtk::Stack,
|
pub widget: gtk::Stack,
|
||||||
screens: RefCell<Vec<Rc<dyn NavigatorScreen>>>,
|
screens: RefCell<Vec<Rc<dyn NavigatorScreen>>>,
|
||||||
old_screens: RefCell<Vec<Rc<dyn NavigatorScreen>>>,
|
old_screens: RefCell<Vec<Rc<dyn NavigatorScreen>>>,
|
||||||
|
|
@ -17,17 +18,22 @@ pub struct Navigator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Navigator {
|
impl Navigator {
|
||||||
pub fn new<W>(empty_screen: &W) -> Rc<Self>
|
pub fn new<W, S>(window: &W, empty_screen: &S) -> Rc<Self>
|
||||||
where
|
where
|
||||||
W: IsA<gtk::Widget>,
|
W: IsA<gtk::Window>,
|
||||||
|
S: IsA<gtk::Widget>,
|
||||||
{
|
{
|
||||||
let widget = gtk::Stack::new();
|
let widget = gtk::Stack::new();
|
||||||
|
widget.set_hhomogeneous(false);
|
||||||
|
widget.set_vhomogeneous(false);
|
||||||
|
widget.set_interpolate_size(true);
|
||||||
widget.set_transition_type(gtk::StackTransitionType::Crossfade);
|
widget.set_transition_type(gtk::StackTransitionType::Crossfade);
|
||||||
widget.set_hexpand(true);
|
widget.set_hexpand(true);
|
||||||
widget.add_named(empty_screen, "empty_screen");
|
widget.add_named(empty_screen, "empty_screen");
|
||||||
widget.show();
|
widget.show();
|
||||||
|
|
||||||
let result = Rc::new(Self {
|
let result = Rc::new(Self {
|
||||||
|
window: window.clone().upcast(),
|
||||||
widget,
|
widget,
|
||||||
screens: RefCell::new(Vec::new()),
|
screens: RefCell::new(Vec::new()),
|
||||||
old_screens: RefCell::new(Vec::new()),
|
old_screens: RefCell::new(Vec::new()),
|
||||||
|
|
@ -48,7 +54,10 @@ impl Navigator {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_back_cb<F>(&self, cb: F) where F: Fn() -> () + 'static {
|
pub fn set_back_cb<F>(&self, cb: F)
|
||||||
|
where
|
||||||
|
F: Fn() -> () + 'static,
|
||||||
|
{
|
||||||
self.back_cb.replace(Some(Box::new(cb)));
|
self.back_cb.replace(Some(Box::new(cb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
42
musicus/src/widgets/navigator_window.rs
Normal file
42
musicus/src/widgets/navigator_window.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
use crate::widgets::{Navigator, NavigatorScreen};
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A window hosting a navigator.
|
||||||
|
pub struct NavigatorWindow {
|
||||||
|
window: libhandy::Window,
|
||||||
|
navigator: Rc<Navigator>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavigatorWindow {
|
||||||
|
/// Create a new navigator window showing an initial screen.
|
||||||
|
pub fn new<S: NavigatorScreen + 'static>(initial_screen: Rc<S>) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let window = libhandy::Window::new();
|
||||||
|
window.set_default_size(600, 424);
|
||||||
|
let placeholder = gtk::Label::new(None);
|
||||||
|
let navigator = Navigator::new(&window, &placeholder);
|
||||||
|
window.add(&navigator.widget);
|
||||||
|
|
||||||
|
let this = Rc::new(Self { window, navigator });
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
this.navigator.set_back_cb(clone!(@strong this => move || {
|
||||||
|
this.window.close();
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
|
||||||
|
this.navigator.clone().replace(initial_screen);
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show the navigator window.
|
||||||
|
pub fn show(&self) {
|
||||||
|
self.window.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::backend::*;
|
use crate::backend::*;
|
||||||
use crate::dialogs::*;
|
use crate::dialogs::*;
|
||||||
|
use crate::editors::TracksEditor;
|
||||||
use crate::screens::*;
|
use crate::screens::*;
|
||||||
use crate::widgets::*;
|
use crate::widgets::*;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
|
|
@ -42,7 +43,7 @@ impl Window {
|
||||||
stack.add_named(&player_screen.widget, "player_screen");
|
stack.add_named(&player_screen.widget, "player_screen");
|
||||||
|
|
||||||
let poe_list = PoeList::new(backend.clone());
|
let poe_list = PoeList::new(backend.clone());
|
||||||
let navigator = Navigator::new(&empty_screen);
|
let navigator = Navigator::new(&window, &empty_screen);
|
||||||
navigator.set_back_cb(clone!(@strong leaflet, @strong sidebar_box => move || {
|
navigator.set_back_cb(clone!(@strong leaflet, @strong sidebar_box => move || {
|
||||||
leaflet.set_visible_child(&sidebar_box);
|
leaflet.set_visible_child(&sidebar_box);
|
||||||
}));
|
}));
|
||||||
|
|
@ -84,13 +85,14 @@ impl Window {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
add_button.connect_clicked(clone!(@strong result => move |_| {
|
add_button.connect_clicked(clone!(@strong result => move |_| {
|
||||||
let editor = TracksEditor::new(result.backend.clone(), &result.window, None, Vec::new());
|
let editor = TracksEditor::new(result.backend.clone(), None, Vec::new());
|
||||||
|
|
||||||
editor.set_callback(clone!(@strong result => move || {
|
editor.set_callback(clone!(@strong result => move || {
|
||||||
result.reload();
|
result.reload();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
editor.show();
|
let window = NavigatorWindow::new(editor);
|
||||||
|
window.show();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
result
|
result
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue