mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-25 20:37:24 +02:00
Use nullable roles instead of default roles
This commit is contained in:
parent
130df0d60e
commit
a371e356f7
16 changed files with 380 additions and 186 deletions
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
adw = { package = "libadwaita", version = "0.7", features = ["v1_6"] }
|
||||
adw = { package = "libadwaita", version = "0.7", features = ["v1_7"] }
|
||||
anyhow = "1"
|
||||
async-channel = "2.3"
|
||||
chrono = "0.4"
|
||||
|
|
|
|||
|
|
@ -3,91 +3,98 @@ using Adw 1;
|
|||
|
||||
template $MusicusPerformerRoleSelectorPopover: Gtk.Popover {
|
||||
styles [
|
||||
"selector"
|
||||
"selector",
|
||||
]
|
||||
|
||||
Gtk.Stack stack {
|
||||
transition-type: slide_left_right;
|
||||
Gtk.Box {
|
||||
orientation: vertical;
|
||||
|
||||
Adw.ToolbarView role_view {
|
||||
[top]
|
||||
Gtk.SearchEntry role_search_entry {
|
||||
placeholder-text: _("Search roles…");
|
||||
margin-start: 8;
|
||||
margin-end: 8;
|
||||
margin-top: 8;
|
||||
margin-bottom: 6;
|
||||
search-changed => $role_search_changed() swapped;
|
||||
activate => $role_activate() swapped;
|
||||
stop-search => $stop_search() swapped;
|
||||
Gtk.CenterBox {
|
||||
margin-start: 6;
|
||||
margin-end: 6;
|
||||
margin-top: 6;
|
||||
|
||||
[center]
|
||||
Adw.InlineViewSwitcher {
|
||||
stack: stack;
|
||||
}
|
||||
|
||||
Gtk.ScrolledWindow role_scrolled_window {
|
||||
height-request: 200;
|
||||
[end]
|
||||
Gtk.Button {
|
||||
icon-name: "edit-clear-symbolic";
|
||||
tooltip-text: _("Reset to default role");
|
||||
margin-start: 6;
|
||||
clicked => $reset_button_clicked() swapped;
|
||||
|
||||
Gtk.ListBox role_list {
|
||||
styles [
|
||||
"selector-list"
|
||||
]
|
||||
|
||||
selection-mode: none;
|
||||
activate-on-single-click: true;
|
||||
}
|
||||
styles [
|
||||
"flat",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Adw.ToolbarView instrument_view {
|
||||
[top]
|
||||
Gtk.Box {
|
||||
margin-start: 8;
|
||||
margin-end: 8;
|
||||
margin-top: 8;
|
||||
margin-bottom: 6;
|
||||
orientation: vertical;
|
||||
Adw.ViewStack stack {
|
||||
Adw.ViewStackPage {
|
||||
name: "role";
|
||||
title: _("Role");
|
||||
|
||||
Gtk.CenterBox {
|
||||
[start]
|
||||
Gtk.Button {
|
||||
styles [
|
||||
"flat"
|
||||
]
|
||||
|
||||
icon-name: "go-previous-symbolic";
|
||||
clicked => $back_button_clicked() swapped;
|
||||
child: Adw.ToolbarView role_view {
|
||||
[top]
|
||||
Gtk.SearchEntry role_search_entry {
|
||||
placeholder-text: _("Search roles…");
|
||||
margin-start: 8;
|
||||
margin-end: 8;
|
||||
margin-top: 8;
|
||||
margin-bottom: 6;
|
||||
search-changed => $role_search_changed() swapped;
|
||||
activate => $role_activate() swapped;
|
||||
stop-search => $stop_search() swapped;
|
||||
}
|
||||
|
||||
[center]
|
||||
Gtk.Label {
|
||||
styles [
|
||||
"heading"
|
||||
]
|
||||
Gtk.ScrolledWindow role_scrolled_window {
|
||||
height-request: 200;
|
||||
|
||||
label: _("Performer");
|
||||
ellipsize: end;
|
||||
margin-start: 6;
|
||||
Gtk.ListBox role_list {
|
||||
styles [
|
||||
"selector-list",
|
||||
]
|
||||
|
||||
selection-mode: none;
|
||||
activate-on-single-click: true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Gtk.SearchEntry instrument_search_entry {
|
||||
placeholder-text: _("Search instruments…");
|
||||
margin-top: 6;
|
||||
search-changed => $instrument_search_changed() swapped;
|
||||
activate => $instrument_activate() swapped;
|
||||
stop-search => $stop_search() swapped;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Gtk.ScrolledWindow instrument_scrolled_window {
|
||||
height-request: 200;
|
||||
Adw.ViewStackPage {
|
||||
name: "instrument";
|
||||
title: _("Instrument");
|
||||
|
||||
Gtk.ListBox instrument_list {
|
||||
styles [
|
||||
"selector-list"
|
||||
]
|
||||
child: Adw.ToolbarView instrument_view {
|
||||
[top]
|
||||
Gtk.SearchEntry instrument_search_entry {
|
||||
placeholder-text: _("Search instruments…");
|
||||
margin-start: 8;
|
||||
margin-end: 8;
|
||||
margin-top: 8;
|
||||
margin-bottom: 6;
|
||||
search-changed => $instrument_search_changed() swapped;
|
||||
activate => $instrument_activate() swapped;
|
||||
stop-search => $stop_search() swapped;
|
||||
}
|
||||
|
||||
selection-mode: none;
|
||||
activate-on-single-click: true;
|
||||
}
|
||||
Gtk.ScrolledWindow instrument_scrolled_window {
|
||||
height-request: 200;
|
||||
|
||||
Gtk.ListBox instrument_list {
|
||||
styles [
|
||||
"selector-list",
|
||||
]
|
||||
|
||||
selection-mode: none;
|
||||
activate-on-single-click: true;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,20 +3,35 @@ using Adw 1;
|
|||
|
||||
template $MusicusRoleSelectorPopover: Gtk.Popover {
|
||||
styles [
|
||||
"selector"
|
||||
"selector",
|
||||
]
|
||||
|
||||
Adw.ToolbarView {
|
||||
[top]
|
||||
Gtk.SearchEntry search_entry {
|
||||
placeholder-text: _("Search roles…");
|
||||
Gtk.Box {
|
||||
spacing: 6;
|
||||
margin-start: 8;
|
||||
margin-end: 8;
|
||||
margin-top: 8;
|
||||
margin-bottom: 6;
|
||||
search-changed => $search_changed() swapped;
|
||||
activate => $activate() swapped;
|
||||
stop-search => $stop_search() swapped;
|
||||
|
||||
Gtk.SearchEntry search_entry {
|
||||
placeholder-text: _("Search roles…");
|
||||
hexpand: true;
|
||||
search-changed => $search_changed() swapped;
|
||||
activate => $activate() swapped;
|
||||
stop-search => $stop_search() swapped;
|
||||
}
|
||||
|
||||
Gtk.Button {
|
||||
icon-name: "edit-clear-symbolic";
|
||||
tooltip-text: _("Reset to default role");
|
||||
clicked => $reset_button_clicked() swapped;
|
||||
|
||||
styles [
|
||||
"flat",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Gtk.ScrolledWindow scrolled_window {
|
||||
|
|
@ -24,7 +39,7 @@ template $MusicusRoleSelectorPopover: Gtk.Popover {
|
|||
|
||||
Gtk.ListBox list_box {
|
||||
styles [
|
||||
"selector-list"
|
||||
"selector-list",
|
||||
]
|
||||
|
||||
selection-mode: none;
|
||||
|
|
|
|||
43
migrations/2025-03-29-142116_nullable_roles/down.sql
Normal file
43
migrations/2025-03-29-142116_nullable_roles/down.sql
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
CREATE TABLE work_persons_old (
|
||||
work_id TEXT NOT NULL REFERENCES works(work_id) ON DELETE CASCADE,
|
||||
person_id TEXT NOT NULL REFERENCES persons(person_id),
|
||||
role_id TEXT NOT NULL REFERENCES roles(role_id),
|
||||
sequence_number INTEGER NOT NULL,
|
||||
PRIMARY KEY (work_id, person_id, role_id)
|
||||
);
|
||||
|
||||
CREATE TABLE recording_persons_old (
|
||||
recording_id TEXT NOT NULL REFERENCES recordings(recording_id) ON DELETE CASCADE,
|
||||
person_id TEXT NOT NULL REFERENCES persons(person_id),
|
||||
role_id TEXT NOT NULL REFERENCES roles(role_id),
|
||||
instrument_id TEXT REFERENCES instruments(instrument_id),
|
||||
sequence_number INTEGER NOT NULL,
|
||||
PRIMARY KEY (recording_id, person_id, role_id, instrument_id)
|
||||
);
|
||||
|
||||
CREATE TABLE recording_ensembles_old (
|
||||
recording_id TEXT NOT NULL REFERENCES recordings(recording_id) ON DELETE CASCADE,
|
||||
ensemble_id TEXT NOT NULL REFERENCES ensembles(ensemble_id),
|
||||
role_id TEXT NOT NULL REFERENCES roles(role_id),
|
||||
sequence_number INTEGER NOT NULL,
|
||||
PRIMARY KEY (recording_id, ensemble_id, role_id)
|
||||
);
|
||||
|
||||
INSERT INTO roles (role_id, name) VALUES ('380d7e09eb2f49c1a90db2ba4acb6ffd', '{"generic":"Composer"}');
|
||||
INSERT INTO roles (role_id, name) VALUES ('28ff0aeb11c041a6916d93e9b4884eef', '{"generic":"Performer"}');
|
||||
|
||||
UPDATE work_persons SET role_id = '380d7e09eb2f49c1a90db2ba4acb6ffd' WHERE role_id IS NULL;
|
||||
UPDATE recording_persons SET role_id = '28ff0aeb11c041a6916d93e9b4884eef' WHERE role_id IS NULL;
|
||||
UPDATE recording_ensembles SET role_id = '28ff0aeb11c041a6916d93e9b4884eef' WHERE role_id IS NULL;
|
||||
|
||||
INSERT INTO work_persons_old SELECT * FROM work_persons;
|
||||
DROP TABLE work_persons;
|
||||
ALTER TABLE work_persons_old RENAME TO work_persons;
|
||||
|
||||
INSERT INTO recording_persons_old SELECT * FROM recording_persons;
|
||||
DROP TABLE recording_persons;
|
||||
ALTER TABLE recording_persons_old RENAME TO recording_persons;
|
||||
|
||||
INSERT INTO recording_ensembles_old SELECT * FROM recording_ensembles;
|
||||
DROP TABLE recording_ensembles;
|
||||
ALTER TABLE recording_ensembles_old RENAME TO recording_ensembles;
|
||||
41
migrations/2025-03-29-142116_nullable_roles/up.sql
Normal file
41
migrations/2025-03-29-142116_nullable_roles/up.sql
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
CREATE TABLE work_persons_new (
|
||||
work_id TEXT NOT NULL REFERENCES works(work_id) ON DELETE CASCADE,
|
||||
person_id TEXT NOT NULL REFERENCES persons(person_id),
|
||||
role_id TEXT REFERENCES roles(role_id),
|
||||
sequence_number INTEGER NOT NULL,
|
||||
PRIMARY KEY (work_id, person_id, sequence_number)
|
||||
);
|
||||
|
||||
CREATE TABLE recording_persons_new (
|
||||
recording_id TEXT NOT NULL REFERENCES recordings(recording_id) ON DELETE CASCADE,
|
||||
person_id TEXT NOT NULL REFERENCES persons(person_id),
|
||||
role_id TEXT REFERENCES roles(role_id),
|
||||
instrument_id TEXT REFERENCES instruments(instrument_id),
|
||||
sequence_number INTEGER NOT NULL,
|
||||
PRIMARY KEY (recording_id, person_id, sequence_number)
|
||||
);
|
||||
|
||||
CREATE TABLE recording_ensembles_new (
|
||||
recording_id TEXT NOT NULL REFERENCES recordings(recording_id) ON DELETE CASCADE,
|
||||
ensemble_id TEXT NOT NULL REFERENCES ensembles(ensemble_id),
|
||||
role_id TEXT REFERENCES roles(role_id),
|
||||
sequence_number INTEGER NOT NULL,
|
||||
PRIMARY KEY (recording_id, ensemble_id, sequence_number)
|
||||
);
|
||||
|
||||
INSERT OR IGNORE INTO work_persons_new SELECT * FROM work_persons;
|
||||
UPDATE work_persons_new SET role_id = NULL WHERE role_id = '380d7e09eb2f49c1a90db2ba4acb6ffd';
|
||||
DROP TABLE work_persons;
|
||||
ALTER TABLE work_persons_new RENAME TO work_persons;
|
||||
|
||||
INSERT OR IGNORE INTO recording_persons_new SELECT * FROM recording_persons;
|
||||
UPDATE recording_persons_new SET role_id = NULL WHERE role_id = '28ff0aeb11c041a6916d93e9b4884eef';
|
||||
DROP TABLE recording_persons;
|
||||
ALTER TABLE recording_persons_new RENAME TO recording_persons;
|
||||
|
||||
INSERT OR IGNORE INTO recording_ensembles_new SELECT * FROM recording_ensembles;
|
||||
UPDATE recording_ensembles_new SET role_id = NULL WHERE role_id = '28ff0aeb11c041a6916d93e9b4884eef';
|
||||
DROP TABLE recording_ensembles;
|
||||
ALTER TABLE recording_ensembles_new RENAME TO recording_ensembles;
|
||||
|
||||
DELETE FROM roles WHERE role_id IN ('380d7e09eb2f49c1a90db2ba4acb6ffd', '28ff0aeb11c041a6916d93e9b4884eef');
|
||||
|
|
@ -21,12 +21,10 @@ pub struct Work {
|
|||
pub instruments: Vec<Instrument>,
|
||||
}
|
||||
|
||||
#[derive(Queryable, Selectable, Clone, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Composer {
|
||||
#[diesel(embed)]
|
||||
pub person: Person,
|
||||
#[diesel(embed)]
|
||||
pub role: Role,
|
||||
pub role: Option<Role>,
|
||||
}
|
||||
|
||||
#[derive(Boxed, Clone, Debug)]
|
||||
|
|
@ -50,14 +48,14 @@ pub struct Recording {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct Performer {
|
||||
pub person: Person,
|
||||
pub role: Role,
|
||||
pub role: Option<Role>,
|
||||
pub instrument: Option<Instrument>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EnsemblePerformer {
|
||||
pub ensemble: Ensemble,
|
||||
pub role: Role,
|
||||
pub role: Option<Role>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -133,12 +131,13 @@ impl Work {
|
|||
.map(|w| Work::from_table(w, connection))
|
||||
.collect::<Result<Vec<Work>>>()?;
|
||||
|
||||
let persons: Vec<Composer> = persons::table
|
||||
.inner_join(work_persons::table.inner_join(roles::table))
|
||||
let persons = work_persons::table
|
||||
.order(work_persons::sequence_number)
|
||||
.filter(work_persons::work_id.eq(&data.work_id))
|
||||
.select(Composer::as_select())
|
||||
.load(connection)?;
|
||||
.load::<tables::WorkPerson>(connection)?
|
||||
.into_iter()
|
||||
.map(|r| Composer::from_table(r, connection))
|
||||
.collect::<Result<Vec<Composer>>>()?;
|
||||
|
||||
let instruments: Vec<Instrument> = instruments::table
|
||||
.inner_join(work_instruments::table)
|
||||
|
|
@ -157,11 +156,10 @@ impl Work {
|
|||
}
|
||||
|
||||
pub fn composers_string(&self) -> Option<String> {
|
||||
// TODO: Include roles except default composer.
|
||||
let composers_string = self
|
||||
.persons
|
||||
.iter()
|
||||
.map(|p| p.person.name.get().to_string())
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
|
|
@ -190,6 +188,34 @@ impl Display for Work {
|
|||
}
|
||||
}
|
||||
|
||||
impl Composer {
|
||||
pub fn from_table(data: tables::WorkPerson, connection: &mut SqliteConnection) -> Result<Self> {
|
||||
let person: Person = persons::table
|
||||
.filter(persons::person_id.eq(&data.person_id))
|
||||
.first(connection)?;
|
||||
|
||||
let role = match &data.role_id {
|
||||
Some(role_id) => Some(
|
||||
roles::table
|
||||
.filter(roles::role_id.eq(role_id))
|
||||
.first::<Role>(connection)?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
|
||||
Ok(Self { person, role })
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Composer {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self.role {
|
||||
Some(role) => format!("{} ({})", self.person.name.get(), role.name.get()).fmt(f),
|
||||
None => self.person.name.get().fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ensemble {
|
||||
pub fn from_table(data: tables::Ensemble, connection: &mut SqliteConnection) -> Result<Self> {
|
||||
let persons: Vec<(Person, Instrument)> = persons::table
|
||||
|
|
@ -297,9 +323,14 @@ impl Performer {
|
|||
.filter(persons::person_id.eq(&data.person_id))
|
||||
.first(connection)?;
|
||||
|
||||
let role: Role = roles::table
|
||||
.filter(roles::role_id.eq(&data.role_id))
|
||||
.first(connection)?;
|
||||
let role = match &data.role_id {
|
||||
Some(role_id) => Some(
|
||||
roles::table
|
||||
.filter(roles::role_id.eq(role_id))
|
||||
.first::<Role>(connection)?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let instrument = match &data.instrument_id {
|
||||
Some(instrument_id) => Some(
|
||||
|
|
@ -320,11 +351,12 @@ impl Performer {
|
|||
|
||||
impl Display for Performer {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self.instrument {
|
||||
Some(instrument) => {
|
||||
match (&self.role, &self.instrument) {
|
||||
(_, Some(instrument)) => {
|
||||
format!("{} ({})", self.person.name.get(), instrument.name.get()).fmt(f)
|
||||
}
|
||||
None => self.person.name.get().fmt(f),
|
||||
(Some(role), _) => format!("{} ({})", self.person.name.get(), role.name.get()).fmt(f),
|
||||
(None, None) => self.person.name.get().fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -340,9 +372,14 @@ impl EnsemblePerformer {
|
|||
|
||||
let ensemble = Ensemble::from_table(ensemble_data, connection)?;
|
||||
|
||||
let role: Role = roles::table
|
||||
.filter(roles::role_id.eq(&data.role_id))
|
||||
.first(connection)?;
|
||||
let role = match &data.role_id {
|
||||
Some(role_id) => Some(
|
||||
roles::table
|
||||
.filter(roles::role_id.eq(role_id))
|
||||
.first::<Role>(connection)?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
|
||||
Ok(Self { ensemble, role })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,19 +81,19 @@ diesel::table! {
|
|||
}
|
||||
|
||||
diesel::table! {
|
||||
recording_ensembles (recording_id, ensemble_id, role_id) {
|
||||
recording_ensembles (recording_id, ensemble_id, sequence_number) {
|
||||
recording_id -> Text,
|
||||
ensemble_id -> Text,
|
||||
role_id -> Text,
|
||||
role_id -> Nullable<Text>,
|
||||
sequence_number -> Integer,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
recording_persons (recording_id, person_id, role_id, instrument_id) {
|
||||
recording_persons (recording_id, person_id, sequence_number) {
|
||||
recording_id -> Text,
|
||||
person_id -> Text,
|
||||
role_id -> Text,
|
||||
role_id -> Nullable<Text>,
|
||||
instrument_id -> Nullable<Text>,
|
||||
sequence_number -> Integer,
|
||||
}
|
||||
|
|
@ -153,10 +153,10 @@ diesel::table! {
|
|||
}
|
||||
|
||||
diesel::table! {
|
||||
work_persons (work_id, person_id, role_id) {
|
||||
work_persons (work_id, person_id, sequence_number) {
|
||||
work_id -> Text,
|
||||
person_id -> Text,
|
||||
role_id -> Text,
|
||||
role_id -> Nullable<Text>,
|
||||
sequence_number -> Integer,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ pub struct Work {
|
|||
pub struct WorkPerson {
|
||||
pub work_id: String,
|
||||
pub person_id: String,
|
||||
pub role_id: String,
|
||||
pub role_id: Option<String>,
|
||||
pub sequence_number: i32,
|
||||
}
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ pub struct Recording {
|
|||
pub struct RecordingPerson {
|
||||
pub recording_id: String,
|
||||
pub person_id: String,
|
||||
pub role_id: String,
|
||||
pub role_id: Option<String>,
|
||||
pub instrument_id: Option<String>,
|
||||
pub sequence_number: i32,
|
||||
}
|
||||
|
|
@ -119,7 +119,7 @@ pub struct RecordingPerson {
|
|||
pub struct RecordingEnsemble {
|
||||
pub recording_id: String,
|
||||
pub ensemble_id: String,
|
||||
pub role_id: String,
|
||||
pub role_id: Option<String>,
|
||||
pub sequence_number: i32,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ impl RecordingEditor {
|
|||
fn new_performer(&self, person: Person) {
|
||||
let performer = Performer {
|
||||
person,
|
||||
role: self.library().performer_default_role().unwrap(),
|
||||
role: None,
|
||||
instrument: None,
|
||||
};
|
||||
|
||||
|
|
@ -299,7 +299,7 @@ impl RecordingEditor {
|
|||
fn new_ensemble_performer(&self, ensemble: Ensemble) {
|
||||
let performer = EnsemblePerformer {
|
||||
ensemble,
|
||||
role: self.library().performer_default_role().unwrap(),
|
||||
role: None,
|
||||
};
|
||||
|
||||
self.add_ensemble_row(performer);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::cell::{OnceCell, RefCell};
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
use gtk::{
|
||||
gdk,
|
||||
glib::{self, clone, subclass::Signal, Properties},
|
||||
|
|
@ -112,7 +113,7 @@ mod imp {
|
|||
role_popover.connect_role_selected(move |_, role| {
|
||||
if let Some(ensemble) = &mut *obj.imp().ensemble.borrow_mut() {
|
||||
obj.imp().role_label.set_label(&role.to_string());
|
||||
ensemble.role = role;
|
||||
ensemble.role = Some(role);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -126,7 +127,7 @@ mod imp {
|
|||
move |_, role| {
|
||||
if let Some(ensemble) = &mut *obj.imp().ensemble.borrow_mut() {
|
||||
obj.imp().role_label.set_label(&role.to_string());
|
||||
ensemble.role = role;
|
||||
ensemble.role = Some(role);
|
||||
};
|
||||
}
|
||||
));
|
||||
|
|
@ -188,7 +189,13 @@ impl RecordingEditorEnsembleRow {
|
|||
|
||||
fn set_ensemble(&self, ensemble: EnsemblePerformer) {
|
||||
self.set_title(&ensemble.ensemble.to_string());
|
||||
self.imp().role_label.set_label(&ensemble.role.to_string());
|
||||
self.imp().role_label.set_label(
|
||||
&ensemble
|
||||
.role
|
||||
.as_ref()
|
||||
.map(ToString::to_string)
|
||||
.unwrap_or_else(|| gettext("Performer")),
|
||||
);
|
||||
self.imp().ensemble.replace(Some(ensemble));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::cell::{OnceCell, RefCell};
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
use gtk::{
|
||||
gdk,
|
||||
glib::{self, clone, subclass::Signal, Properties},
|
||||
|
|
@ -110,17 +111,29 @@ mod imp {
|
|||
let role_popover = PerformerRoleSelectorPopover::new(self.library.get().unwrap());
|
||||
|
||||
let obj = self.obj().to_owned();
|
||||
role_popover.connect_selected(move |_, role, instrument| {
|
||||
role_popover.connect_reset(move |_| {
|
||||
if let Some(performer) = &mut *obj.imp().performer.borrow_mut() {
|
||||
let label = match &instrument {
|
||||
Some(instrument) => instrument.to_string(),
|
||||
None => role.to_string(),
|
||||
};
|
||||
obj.imp().role_label.set_label(&gettext("Performer"));
|
||||
performer.role = None;
|
||||
performer.instrument = None;
|
||||
}
|
||||
});
|
||||
|
||||
obj.imp().role_label.set_label(&label);
|
||||
let obj = self.obj().to_owned();
|
||||
role_popover.connect_role_selected(move |_, role| {
|
||||
if let Some(performer) = &mut *obj.imp().performer.borrow_mut() {
|
||||
obj.imp().role_label.set_label(&role.to_string());
|
||||
performer.role = Some(role);
|
||||
performer.instrument = None;
|
||||
}
|
||||
});
|
||||
|
||||
performer.role = role;
|
||||
performer.instrument = instrument;
|
||||
let obj = self.obj().to_owned();
|
||||
role_popover.connect_instrument_selected(move |_, instrument| {
|
||||
if let Some(performer) = &mut *obj.imp().performer.borrow_mut() {
|
||||
obj.imp().role_label.set_label(&instrument.to_string());
|
||||
performer.role = None;
|
||||
performer.instrument = Some(instrument);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -134,7 +147,7 @@ mod imp {
|
|||
move |_, role| {
|
||||
if let Some(performer) = &mut *obj.imp().performer.borrow_mut() {
|
||||
obj.imp().role_label.set_label(&role.to_string());
|
||||
performer.role = role;
|
||||
performer.role = Some(role);
|
||||
performer.instrument = None;
|
||||
};
|
||||
}
|
||||
|
|
@ -153,7 +166,7 @@ mod imp {
|
|||
move |_, instrument| {
|
||||
if let Some(performer) = &mut *obj.imp().performer.borrow_mut() {
|
||||
obj.imp().role_label.set_label(&instrument.to_string());
|
||||
performer.role = obj.library().performer_default_role().unwrap();
|
||||
performer.role = None;
|
||||
performer.instrument = Some(instrument);
|
||||
};
|
||||
}
|
||||
|
|
@ -215,7 +228,11 @@ impl RecordingEditorPerformerRow {
|
|||
|
||||
let label = match &performer.instrument {
|
||||
Some(instrument) => instrument.to_string(),
|
||||
None => performer.role.to_string(),
|
||||
None => performer
|
||||
.role
|
||||
.as_ref()
|
||||
.map(ToString::to_string)
|
||||
.unwrap_or_else(|| gettext("Performer")),
|
||||
};
|
||||
|
||||
self.imp().role_label.set_label(&label.to_string());
|
||||
|
|
|
|||
|
|
@ -233,8 +233,7 @@ impl WorkEditor {
|
|||
}
|
||||
|
||||
fn add_composer(&self, person: Person) {
|
||||
let role = self.library().composer_default_role().unwrap();
|
||||
let composer = Composer { person, role };
|
||||
let composer = Composer { person, role: None };
|
||||
self.add_composer_row(composer);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::cell::{OnceCell, RefCell};
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
use gtk::{
|
||||
gdk,
|
||||
glib::{self, clone, subclass::Signal, Properties},
|
||||
|
|
@ -108,11 +109,19 @@ mod imp {
|
|||
|
||||
let role_popover = RoleSelectorPopover::new(self.library.get().unwrap());
|
||||
|
||||
let obj = self.obj().to_owned();
|
||||
role_popover.connect_reset(move |_| {
|
||||
if let Some(composer) = &mut *obj.imp().composer.borrow_mut() {
|
||||
obj.imp().role_label.set_label(&gettext("Composer"));
|
||||
composer.role = None;
|
||||
}
|
||||
});
|
||||
|
||||
let obj = self.obj().to_owned();
|
||||
role_popover.connect_role_selected(move |_, role| {
|
||||
if let Some(composer) = &mut *obj.imp().composer.borrow_mut() {
|
||||
obj.imp().role_label.set_label(&role.to_string());
|
||||
composer.role = role;
|
||||
composer.role = Some(role);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -126,7 +135,7 @@ mod imp {
|
|||
move |_, role| {
|
||||
if let Some(composer) = &mut *obj.imp().composer.borrow_mut() {
|
||||
obj.imp().role_label.set_label(&role.to_string());
|
||||
composer.role = role;
|
||||
composer.role = Some(role);
|
||||
};
|
||||
}
|
||||
));
|
||||
|
|
@ -184,7 +193,13 @@ impl WorkEditorComposerRow {
|
|||
|
||||
fn set_composer(&self, composer: Composer) {
|
||||
self.set_title(&composer.person.to_string());
|
||||
self.imp().role_label.set_label(&composer.role.to_string());
|
||||
self.imp().role_label.set_label(
|
||||
&composer
|
||||
.role
|
||||
.as_ref()
|
||||
.map(ToString::to_string)
|
||||
.unwrap_or_else(|| gettext("Composer")),
|
||||
);
|
||||
self.imp().composer.replace(Some(composer));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -933,22 +933,6 @@ impl Library {
|
|||
Ok(ensembles)
|
||||
}
|
||||
|
||||
pub fn composer_default_role(&self) -> Result<Role> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
Ok(roles::table
|
||||
.filter(roles::role_id.eq("380d7e09eb2f49c1a90db2ba4acb6ffd"))
|
||||
.first::<Role>(connection)?)
|
||||
}
|
||||
|
||||
pub fn performer_default_role(&self) -> Result<Role> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
Ok(roles::table
|
||||
.filter(roles::role_id.eq("28ff0aeb11c041a6916d93e9b4884eef"))
|
||||
.first::<Role>(connection)?)
|
||||
}
|
||||
|
||||
pub fn create_person(&self, name: TranslatedString) -> Result<Person> {
|
||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||
|
||||
|
|
@ -1171,7 +1155,7 @@ impl Library {
|
|||
let composer_data = tables::WorkPerson {
|
||||
work_id: work_id.clone(),
|
||||
person_id: composer.person.person_id,
|
||||
role_id: composer.role.role_id,
|
||||
role_id: composer.role.map(|r| r.role_id),
|
||||
sequence_number: index as i32,
|
||||
};
|
||||
|
||||
|
|
@ -1295,7 +1279,7 @@ impl Library {
|
|||
let composer_data = tables::WorkPerson {
|
||||
work_id: work_id.to_string(),
|
||||
person_id: composer.person.person_id,
|
||||
role_id: composer.role.role_id,
|
||||
role_id: composer.role.map(|r| r.role_id),
|
||||
sequence_number: index as i32,
|
||||
};
|
||||
|
||||
|
|
@ -1425,7 +1409,7 @@ impl Library {
|
|||
let recording_person_data = tables::RecordingPerson {
|
||||
recording_id: recording_id.clone(),
|
||||
person_id: performer.person.person_id,
|
||||
role_id: performer.role.role_id,
|
||||
role_id: performer.role.map(|r| r.role_id),
|
||||
instrument_id: performer.instrument.map(|i| i.instrument_id),
|
||||
sequence_number: index as i32,
|
||||
};
|
||||
|
|
@ -1439,7 +1423,7 @@ impl Library {
|
|||
let recording_ensemble_data = tables::RecordingEnsemble {
|
||||
recording_id: recording_id.clone(),
|
||||
ensemble_id: ensemble.ensemble.ensemble_id,
|
||||
role_id: ensemble.role.role_id,
|
||||
role_id: ensemble.role.map(|r| r.role_id),
|
||||
sequence_number: index as i32,
|
||||
};
|
||||
|
||||
|
|
@ -1485,7 +1469,7 @@ impl Library {
|
|||
let recording_person_data = tables::RecordingPerson {
|
||||
recording_id: recording_id.to_string(),
|
||||
person_id: performer.person.person_id,
|
||||
role_id: performer.role.role_id,
|
||||
role_id: performer.role.map(|r| r.role_id),
|
||||
instrument_id: performer.instrument.map(|i| i.instrument_id),
|
||||
sequence_number: index as i32,
|
||||
};
|
||||
|
|
@ -1503,7 +1487,7 @@ impl Library {
|
|||
let recording_ensemble_data = tables::RecordingEnsemble {
|
||||
recording_id: recording_id.to_string(),
|
||||
ensemble_id: ensemble.ensemble.ensemble_id,
|
||||
role_id: ensemble.role.role_id,
|
||||
role_id: ensemble.role.map(|r| r.role_id),
|
||||
sequence_number: index as i32,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -29,9 +29,7 @@ mod imp {
|
|||
pub instruments: RefCell<Vec<Instrument>>,
|
||||
|
||||
#[template_child]
|
||||
pub stack: TemplateChild<gtk::Stack>,
|
||||
#[template_child]
|
||||
pub role_view: TemplateChild<adw::ToolbarView>,
|
||||
pub stack: TemplateChild<adw::ViewStack>,
|
||||
#[template_child]
|
||||
pub role_search_entry: TemplateChild<gtk::SearchEntry>,
|
||||
#[template_child]
|
||||
|
|
@ -39,8 +37,6 @@ mod imp {
|
|||
#[template_child]
|
||||
pub role_list: TemplateChild<gtk::ListBox>,
|
||||
#[template_child]
|
||||
pub instrument_view: TemplateChild<adw::ToolbarView>,
|
||||
#[template_child]
|
||||
pub instrument_search_entry: TemplateChild<gtk::SearchEntry>,
|
||||
#[template_child]
|
||||
pub instrument_scrolled_window: TemplateChild<gtk::ScrolledWindow>,
|
||||
|
|
@ -71,21 +67,35 @@ mod imp {
|
|||
|
||||
self.obj().connect_visible_notify(|obj| {
|
||||
if obj.is_visible() {
|
||||
obj.imp().stack.set_visible_child(&*obj.imp().role_view);
|
||||
obj.imp().role_search_entry.set_text("");
|
||||
obj.imp().role_search_entry.grab_focus();
|
||||
obj.imp().role_scrolled_window.vadjustment().set_value(0.0);
|
||||
obj.imp().instrument_search_entry.set_text("");
|
||||
obj.imp()
|
||||
.instrument_scrolled_window
|
||||
.vadjustment()
|
||||
.set_value(0.0);
|
||||
|
||||
if obj.imp().stack.visible_child_name().as_deref() == Some("role") {
|
||||
obj.imp().role_search_entry.grab_focus();
|
||||
} else {
|
||||
obj.imp().instrument_search_entry.grab_focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self.obj().search_roles("");
|
||||
self.obj().search_instruments("");
|
||||
}
|
||||
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
Signal::builder("selected")
|
||||
.param_types([Role::static_type(), Instrument::static_type()])
|
||||
Signal::builder("reset").build(),
|
||||
Signal::builder("role-selected")
|
||||
.param_types([Role::static_type()])
|
||||
.build(),
|
||||
Signal::builder("instrument-selected")
|
||||
.param_types([Instrument::static_type()])
|
||||
.build(),
|
||||
Signal::builder("create-role").build(),
|
||||
Signal::builder("create-instrument").build(),
|
||||
|
|
@ -100,7 +110,7 @@ mod imp {
|
|||
// TODO: Fix focus.
|
||||
fn focus(&self, direction_type: gtk::DirectionType) -> bool {
|
||||
if direction_type == gtk::DirectionType::Down {
|
||||
if self.stack.visible_child() == Some(self.role_list.get().upcast()) {
|
||||
if self.stack.visible_child_name().as_deref() == Some("role") {
|
||||
self.role_list.child_focus(direction_type)
|
||||
} else {
|
||||
self.instrument_list.child_focus(direction_type)
|
||||
|
|
@ -125,15 +135,34 @@ impl PerformerRoleSelectorPopover {
|
|||
glib::Object::builder().property("library", library).build()
|
||||
}
|
||||
|
||||
pub fn connect_selected<F: Fn(&Self, Role, Option<Instrument>) + 'static>(
|
||||
pub fn connect_reset<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("reset", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
f(&obj);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn connect_role_selected<F: Fn(&Self, Role) + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> glib::SignalHandlerId {
|
||||
self.connect_local("selected", true, move |values| {
|
||||
self.connect_local("role-selected", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
let role = values[1].get::<Role>().unwrap();
|
||||
let instrument = values[2].get::<Option<Instrument>>().unwrap();
|
||||
f(&obj, role, instrument);
|
||||
f(&obj, role);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn connect_instrument_selected<F: Fn(&Self, Instrument) + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> glib::SignalHandlerId {
|
||||
self.connect_local("instrument-selected", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
let role = values[1].get::<Instrument>().unwrap();
|
||||
f(&obj, role);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
|
@ -154,6 +183,12 @@ impl PerformerRoleSelectorPopover {
|
|||
})
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn reset_button_clicked(&self) {
|
||||
self.emit_by_name::<()>("reset", &[]);
|
||||
self.popdown();
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn role_search_changed(&self, entry: >k::SearchEntry) {
|
||||
self.search_roles(&entry.text());
|
||||
|
|
@ -168,12 +203,6 @@ impl PerformerRoleSelectorPopover {
|
|||
}
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn back_button_clicked(&self) {
|
||||
self.imp().stack.set_visible_child(&*self.imp().role_view);
|
||||
self.imp().role_search_entry.grab_focus();
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn instrument_search_changed(&self, entry: >k::SearchEntry) {
|
||||
self.search_instruments(&entry.text());
|
||||
|
|
@ -293,27 +322,12 @@ impl PerformerRoleSelectorPopover {
|
|||
}
|
||||
|
||||
fn select_role(&self, role: Role) {
|
||||
if role == self.library().performer_default_role().unwrap() {
|
||||
self.imp().instrument_search_entry.set_text("");
|
||||
self.imp().instrument_search_entry.grab_focus();
|
||||
self.imp()
|
||||
.instrument_scrolled_window
|
||||
.vadjustment()
|
||||
.set_value(0.0);
|
||||
self.imp()
|
||||
.stack
|
||||
.set_visible_child(&*self.imp().instrument_view);
|
||||
|
||||
self.search_instruments("");
|
||||
} else {
|
||||
self.emit_by_name::<()>("selected", &[&role, &None::<Instrument>]);
|
||||
self.popdown();
|
||||
}
|
||||
self.emit_by_name::<()>("role-selected", &[&role]);
|
||||
self.popdown();
|
||||
}
|
||||
|
||||
fn select_instrument(&self, instrument: Instrument) {
|
||||
let role = self.library().performer_default_role().unwrap();
|
||||
self.emit_by_name::<()>("selected", &[&role, &instrument]);
|
||||
self.emit_by_name::<()>("instrument-selected", &[&instrument]);
|
||||
self.popdown();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ mod imp {
|
|||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
Signal::builder("reset").build(),
|
||||
Signal::builder("role-selected")
|
||||
.param_types([Role::static_type()])
|
||||
.build(),
|
||||
|
|
@ -101,6 +102,14 @@ impl RoleSelectorPopover {
|
|||
glib::Object::builder().property("library", library).build()
|
||||
}
|
||||
|
||||
pub fn connect_reset<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("reset", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
f(&obj);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn connect_role_selected<F: Fn(&Self, Role) + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
|
|
@ -140,6 +149,12 @@ impl RoleSelectorPopover {
|
|||
self.popdown();
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn reset_button_clicked(&self) {
|
||||
self.emit_by_name::<()>("reset", &[]);
|
||||
self.popdown();
|
||||
}
|
||||
|
||||
fn search(&self, search: &str) {
|
||||
let imp = self.imp();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue