mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Use editor widget for performance editor
This commit is contained in:
parent
d92fd419d3
commit
7eff62b5a4
7 changed files with 170 additions and 66 deletions
|
|
@ -1,11 +1,10 @@
|
|||
use crate::backend::Backend;
|
||||
use crate::database::*;
|
||||
use crate::selectors::{EnsembleSelector, InstrumentSelector, PersonSelector};
|
||||
use crate::widgets::{Navigator, NavigatorScreen};
|
||||
use crate::widgets::{Editor, Navigator, NavigatorScreen, Section, ButtonRow, Widget};
|
||||
use gettextrs::gettext;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk_macros::get_widget;
|
||||
use libadwaita::prelude::*;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
|
@ -13,11 +12,10 @@ use std::rc::Rc;
|
|||
/// A dialog for editing a performance within a recording.
|
||||
pub struct PerformanceEditor {
|
||||
backend: Rc<Backend>,
|
||||
widget: gtk::Box,
|
||||
save_button: gtk::Button,
|
||||
person_row: libadwaita::ActionRow,
|
||||
ensemble_row: libadwaita::ActionRow,
|
||||
role_row: libadwaita::ActionRow,
|
||||
editor: Editor,
|
||||
person_row: ButtonRow,
|
||||
ensemble_row: ButtonRow,
|
||||
role_row: ButtonRow,
|
||||
reset_role_button: gtk::Button,
|
||||
person: RefCell<Option<Person>>,
|
||||
ensemble: RefCell<Option<Ensemble>>,
|
||||
|
|
@ -29,25 +27,49 @@ pub struct PerformanceEditor {
|
|||
impl PerformanceEditor {
|
||||
/// Create a new performance editor.
|
||||
pub fn new(backend: Rc<Backend>, performance: Option<Performance>) -> Rc<Self> {
|
||||
// Create UI
|
||||
let editor = Editor::new();
|
||||
editor.set_title("Performance");
|
||||
editor.set_may_save(false);
|
||||
|
||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/performance_editor.ui");
|
||||
let performer_list = gtk::ListBoxBuilder::new()
|
||||
.selection_mode(gtk::SelectionMode::None)
|
||||
.build();
|
||||
|
||||
get_widget!(builder, gtk::Box, widget);
|
||||
get_widget!(builder, gtk::Button, back_button);
|
||||
get_widget!(builder, gtk::Button, save_button);
|
||||
get_widget!(builder, gtk::Button, person_button);
|
||||
get_widget!(builder, gtk::Button, ensemble_button);
|
||||
get_widget!(builder, gtk::Button, role_button);
|
||||
get_widget!(builder, gtk::Button, reset_role_button);
|
||||
get_widget!(builder, libadwaita::ActionRow, person_row);
|
||||
get_widget!(builder, libadwaita::ActionRow, ensemble_row);
|
||||
get_widget!(builder, libadwaita::ActionRow, role_row);
|
||||
let person_row = ButtonRow::new("Person", "Select");
|
||||
let ensemble_row = ButtonRow::new("Ensemble", "Select");
|
||||
|
||||
performer_list.append(&person_row.get_widget());
|
||||
performer_list.append(&ensemble_row.get_widget());
|
||||
|
||||
let performer_section = Section::new(&gettext("Performer"), &performer_list);
|
||||
performer_section.set_subtitle(
|
||||
&gettext("Select either a person or an ensemble as a performer."));
|
||||
|
||||
let role_list = gtk::ListBoxBuilder::new()
|
||||
.selection_mode(gtk::SelectionMode::None)
|
||||
.build();
|
||||
|
||||
let reset_role_button = gtk::ButtonBuilder::new()
|
||||
.icon_name("user-trash-symbolic")
|
||||
.valign(gtk::Align::Center)
|
||||
.visible(false)
|
||||
.build();
|
||||
|
||||
let role_row = ButtonRow::new("Role", "Select");
|
||||
role_row.widget.add_suffix(&reset_role_button);
|
||||
|
||||
role_list.append(&role_row.get_widget());
|
||||
|
||||
let role_section = Section::new(&gettext("Role"), &role_list);
|
||||
role_section.set_subtitle(
|
||||
&gettext("Optionally, choose a role to specify what the performer does."));
|
||||
|
||||
editor.add_content(&performer_section);
|
||||
editor.add_content(&role_section);
|
||||
|
||||
let this = Rc::new(PerformanceEditor {
|
||||
backend,
|
||||
widget,
|
||||
save_button,
|
||||
editor,
|
||||
person_row,
|
||||
ensemble_row,
|
||||
role_row,
|
||||
|
|
@ -59,32 +81,29 @@ impl PerformanceEditor {
|
|||
navigator: RefCell::new(None),
|
||||
});
|
||||
|
||||
// Connect signals and callbacks
|
||||
|
||||
back_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
this.editor.set_back_cb(clone!(@strong this => move || {
|
||||
let navigator = this.navigator.borrow().clone();
|
||||
if let Some(navigator) = navigator {
|
||||
navigator.pop();
|
||||
}
|
||||
}));
|
||||
|
||||
this.save_button
|
||||
.connect_clicked(clone!(@strong this => move |_| {
|
||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||
cb(Performance {
|
||||
person: this.person.borrow().clone(),
|
||||
ensemble: this.ensemble.borrow().clone(),
|
||||
role: this.role.borrow().clone(),
|
||||
});
|
||||
}
|
||||
this.editor.set_save_cb(clone!(@weak this => move || {
|
||||
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||
cb(Performance {
|
||||
person: this.person.borrow().clone(),
|
||||
ensemble: this.ensemble.borrow().clone(),
|
||||
role: this.role.borrow().clone(),
|
||||
});
|
||||
}
|
||||
|
||||
let navigator = this.navigator.borrow().clone();
|
||||
if let Some(navigator) = navigator {
|
||||
navigator.pop();
|
||||
}
|
||||
}));
|
||||
let navigator = this.navigator.borrow().clone();
|
||||
if let Some(navigator) = navigator {
|
||||
navigator.pop();
|
||||
}
|
||||
}));
|
||||
|
||||
person_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
this.person_row.set_cb(clone!(@weak this => move || {
|
||||
let navigator = this.navigator.borrow().clone();
|
||||
if let Some(navigator) = navigator {
|
||||
let selector = PersonSelector::new(this.backend.clone());
|
||||
|
|
@ -101,7 +120,7 @@ impl PerformanceEditor {
|
|||
}
|
||||
}));
|
||||
|
||||
ensemble_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
this.ensemble_row.set_cb(clone!(@weak this => move || {
|
||||
let navigator = this.navigator.borrow().clone();
|
||||
if let Some(navigator) = navigator {
|
||||
let selector = EnsembleSelector::new(this.backend.clone());
|
||||
|
|
@ -118,7 +137,7 @@ impl PerformanceEditor {
|
|||
}
|
||||
}));
|
||||
|
||||
role_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
this.role_row.set_cb(clone!(@weak this => move || {
|
||||
let navigator = this.navigator.borrow().clone();
|
||||
if let Some(navigator) = navigator {
|
||||
let selector = InstrumentSelector::new(this.backend.clone());
|
||||
|
|
@ -133,11 +152,10 @@ impl PerformanceEditor {
|
|||
}
|
||||
}));
|
||||
|
||||
this.reset_role_button
|
||||
.connect_clicked(clone!(@strong this => move |_| {
|
||||
this.show_role(None);
|
||||
this.role.replace(None);
|
||||
}));
|
||||
this.reset_role_button.connect_clicked(clone!(@weak this => move |_| {
|
||||
this.show_role(None);
|
||||
this.role.replace(None);
|
||||
}));
|
||||
|
||||
// Initialize
|
||||
|
||||
|
|
@ -167,11 +185,9 @@ impl PerformanceEditor {
|
|||
/// Update the UI according to person.
|
||||
fn show_person(&self, person: Option<&Person>) {
|
||||
if let Some(person) = person {
|
||||
self.person_row.set_title(Some(&gettext("Person")));
|
||||
self.person_row.set_subtitle(Some(&person.name_fl()));
|
||||
self.save_button.set_sensitive(true);
|
||||
self.editor.set_may_save(true);
|
||||
} else {
|
||||
self.person_row.set_title(Some(&gettext("Select a person")));
|
||||
self.person_row.set_subtitle(None);
|
||||
}
|
||||
}
|
||||
|
|
@ -179,11 +195,9 @@ impl PerformanceEditor {
|
|||
/// Update the UI according to ensemble.
|
||||
fn show_ensemble(&self, ensemble: Option<&Ensemble>) {
|
||||
if let Some(ensemble) = ensemble {
|
||||
self.ensemble_row.set_title(Some(&gettext("Ensemble")));
|
||||
self.ensemble_row.set_subtitle(Some(&ensemble.name));
|
||||
self.save_button.set_sensitive(true);
|
||||
self.editor.set_may_save(true);
|
||||
} else {
|
||||
self.ensemble_row.set_title(Some(&gettext("Select an ensemble")));
|
||||
self.ensemble_row.set_subtitle(None);
|
||||
}
|
||||
}
|
||||
|
|
@ -191,11 +205,9 @@ impl PerformanceEditor {
|
|||
/// Update the UI according to role.
|
||||
fn show_role(&self, role: Option<&Instrument>) {
|
||||
if let Some(role) = role {
|
||||
self.role_row.set_title(Some(&gettext("Role")));
|
||||
self.role_row.set_subtitle(Some(&role.name));
|
||||
self.reset_role_button.show();
|
||||
} else {
|
||||
self.role_row.set_title(Some(&gettext("Select a role")));
|
||||
self.role_row.set_subtitle(None);
|
||||
self.reset_role_button.hide();
|
||||
}
|
||||
|
|
@ -208,7 +220,7 @@ impl NavigatorScreen for PerformanceEditor {
|
|||
}
|
||||
|
||||
fn get_widget(&self) -> gtk::Widget {
|
||||
self.widget.clone().upcast()
|
||||
self.editor.widget.clone().upcast()
|
||||
}
|
||||
|
||||
fn detach_navigator(&self) {
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ sources = files(
|
|||
'selectors/recording.rs',
|
||||
'selectors/selector.rs',
|
||||
'selectors/work.rs',
|
||||
'widgets/button_row.rs',
|
||||
'widgets/editor.rs',
|
||||
'widgets/entry_row.rs',
|
||||
'widgets/upload_section.rs',
|
||||
|
|
|
|||
51
src/widgets/button_row.rs
Normal file
51
src/widgets/button_row.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
use super::Widget;
|
||||
use gtk::prelude::*;
|
||||
use libadwaita::prelude::*;
|
||||
|
||||
/// A list box row with a single button.
|
||||
pub struct ButtonRow {
|
||||
/// The actual GTK widget.
|
||||
pub widget: libadwaita::ActionRow,
|
||||
|
||||
/// The managed button.
|
||||
button: gtk::Button,
|
||||
}
|
||||
|
||||
impl ButtonRow {
|
||||
/// Create a new button row.
|
||||
pub fn new(title: &str, label: &str) -> Self {
|
||||
let button = gtk::ButtonBuilder::new()
|
||||
.valign(gtk::Align::Center)
|
||||
.label(label)
|
||||
.build();
|
||||
|
||||
let widget = libadwaita::ActionRowBuilder::new()
|
||||
.activatable(true)
|
||||
.activatable_widget(&button)
|
||||
.title(title)
|
||||
.build();
|
||||
|
||||
widget.add_suffix(&button);
|
||||
|
||||
Self {
|
||||
widget,
|
||||
button,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the subtitle of the row.
|
||||
pub fn set_subtitle(&self, subtitle: Option<&str>) {
|
||||
self.widget.set_subtitle(subtitle);
|
||||
}
|
||||
|
||||
/// Set the closure to be called on activation
|
||||
pub fn set_cb<F: Fn() + 'static>(&self, cb: F) {
|
||||
self.button.connect_clicked(move |_| cb());
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for ButtonRow {
|
||||
fn get_widget(&self) -> gtk::Widget {
|
||||
self.widget.clone().upcast()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
use super::Widget;
|
||||
use gio::prelude::*;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
|
|
@ -61,6 +62,11 @@ impl Editor {
|
|||
self.window_title.set_title(Some(title));
|
||||
}
|
||||
|
||||
/// Set whether the user should be able to click the save button.
|
||||
pub fn set_may_save(&self, save: bool) {
|
||||
self.save_button.set_sensitive(save);
|
||||
}
|
||||
|
||||
pub fn set_save_cb<F: Fn() + 'static>(&self, cb: F) {
|
||||
self.save_button.connect_clicked(move |_| cb());
|
||||
}
|
||||
|
|
@ -79,7 +85,7 @@ impl Editor {
|
|||
}
|
||||
|
||||
/// Add content to the bottom of the content area.
|
||||
pub fn add_content<W: IsA<gtk::Widget>>(&self, content: &W) {
|
||||
self.content_box.append(content);
|
||||
pub fn add_content<W: Widget>(&self, content: &W) {
|
||||
self.content_box.append(&content.get_widget());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
use gtk::prelude::*;
|
||||
|
||||
pub mod button_row;
|
||||
pub use button_row::*;
|
||||
|
||||
pub mod editor;
|
||||
pub use editor::*;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use super::Widget;
|
||||
use gtk::prelude::*;
|
||||
use gtk_macros::get_widget;
|
||||
|
||||
|
|
@ -9,27 +10,38 @@ pub struct Section {
|
|||
|
||||
/// The box containing the title and action buttons.
|
||||
title_box: gtk::Box,
|
||||
|
||||
/// An optional subtitle below the title.
|
||||
subtitle_label: gtk::Label,
|
||||
}
|
||||
|
||||
impl Section {
|
||||
/// Create a new section.
|
||||
pub fn new<W: IsA<gtk::Widget>>(title: &str, content: &W) -> Self {
|
||||
pub fn new<W: Widget>(title: &str, content: &W) -> Self {
|
||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/section.ui");
|
||||
|
||||
get_widget!(builder, gtk::Box, widget);
|
||||
get_widget!(builder, gtk::Box, title_box);
|
||||
get_widget!(builder, gtk::Label, title_label);
|
||||
get_widget!(builder, gtk::Label, subtitle_label);
|
||||
get_widget!(builder, gtk::Frame, frame);
|
||||
|
||||
title_label.set_label(title);
|
||||
frame.set_child(Some(content));
|
||||
frame.set_child(Some(&content.get_widget()));
|
||||
|
||||
Self {
|
||||
widget,
|
||||
title_box,
|
||||
subtitle_label,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a subtitle below the title.
|
||||
pub fn set_subtitle(&self, subtitle: &str) {
|
||||
self.subtitle_label.set_label(subtitle);
|
||||
self.subtitle_label.show();
|
||||
}
|
||||
|
||||
/// Add an action button. This should by definition be something that is
|
||||
/// doing something with the child widget that is applicable in all
|
||||
/// situations where the widget is visible. The new button will be packed
|
||||
|
|
@ -46,3 +58,9 @@ impl Section {
|
|||
self.title_box.append(&button);
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Section {
|
||||
fn get_widget(&self) -> gtk::Widget {
|
||||
self.widget.clone().upcast()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue