mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Fully functional person editor
This commit is contained in:
parent
f49f23a501
commit
3dc601e0f0
10 changed files with 214 additions and 43 deletions
|
|
@ -67,9 +67,12 @@ mod imp {
|
|||
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![Signal::builder("instrument-selected")
|
||||
.param_types([Instrument::static_type()])
|
||||
.build()]
|
||||
vec![
|
||||
Signal::builder("instrument-selected")
|
||||
.param_types([Instrument::static_type()])
|
||||
.build(),
|
||||
Signal::builder("create").build(),
|
||||
]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
|
|
@ -113,6 +116,14 @@ impl MusicusInstrumentSelectorPopover {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn connect_create<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("create", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
f(&obj);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn search_changed(&self, entry: >k::SearchEntry) {
|
||||
self.search(&entry.text());
|
||||
|
|
@ -135,7 +146,12 @@ impl MusicusInstrumentSelectorPopover {
|
|||
fn search(&self, search: &str) {
|
||||
let imp = self.imp();
|
||||
|
||||
let instruments = imp.library.get().unwrap().search_instruments(search).unwrap();
|
||||
let instruments = imp
|
||||
.library
|
||||
.get()
|
||||
.unwrap()
|
||||
.search_instruments(search)
|
||||
.unwrap();
|
||||
|
||||
imp.list_box.remove_all();
|
||||
|
||||
|
|
@ -182,7 +198,7 @@ impl MusicusInstrumentSelectorPopover {
|
|||
}
|
||||
|
||||
fn create(&self) {
|
||||
log::info!("Create instrument!");
|
||||
self.emit_by_name::<()>("create", &[]);
|
||||
self.popdown();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,30 @@
|
|||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::glib;
|
||||
use std::cell::OnceCell;
|
||||
|
||||
use crate::{db::models::Person, editor::translation_editor::MusicusTranslationEditor};
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
use gtk::glib::{self, subclass::Signal};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
db::models::Person, editor::translation_editor::MusicusTranslationEditor,
|
||||
library::MusicusLibrary,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
||||
#[template(file = "data/ui/person_editor.blp")]
|
||||
pub struct MusicusPersonEditor {
|
||||
pub navigation: OnceCell<adw::NavigationView>,
|
||||
pub library: OnceCell<MusicusLibrary>,
|
||||
pub person_id: OnceCell<String>,
|
||||
|
||||
#[template_child]
|
||||
pub name_editor: TemplateChild<MusicusTranslationEditor>,
|
||||
#[template_child]
|
||||
pub save_button: TemplateChild<gtk::Button>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
|
@ -30,7 +44,18 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for MusicusPersonEditor {}
|
||||
impl ObjectImpl for MusicusPersonEditor {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![Signal::builder("created")
|
||||
.param_types([Person::static_type()])
|
||||
.build()]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for MusicusPersonEditor {}
|
||||
impl NavigationPageImpl for MusicusPersonEditor {}
|
||||
}
|
||||
|
|
@ -42,13 +67,46 @@ glib::wrapper! {
|
|||
|
||||
#[gtk::template_callbacks]
|
||||
impl MusicusPersonEditor {
|
||||
pub fn new(person: Option<&Person>) -> Self {
|
||||
pub fn new(
|
||||
navigation: &adw::NavigationView,
|
||||
library: &MusicusLibrary,
|
||||
person: Option<&Person>,
|
||||
) -> Self {
|
||||
let obj: Self = glib::Object::new();
|
||||
|
||||
obj.imp().navigation.set(navigation.to_owned()).unwrap();
|
||||
obj.imp().library.set(library.to_owned()).unwrap();
|
||||
|
||||
if let Some(person) = person {
|
||||
obj.imp().save_button.set_label(&gettext("Save changes"));
|
||||
obj.imp().person_id.set(person.person_id.clone()).unwrap();
|
||||
obj.imp().name_editor.set_translation(&person.name);
|
||||
}
|
||||
|
||||
obj
|
||||
}
|
||||
|
||||
pub fn connect_created<F: Fn(&Self, Person) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("created", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
let person = values[1].get::<Person>().unwrap();
|
||||
f(&obj, person);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn save(&self, _: >k::Button) {
|
||||
let library = self.imp().library.get().unwrap();
|
||||
let name = self.imp().name_editor.translation();
|
||||
|
||||
if let Some(person_id) = self.imp().person_id.get() {
|
||||
library.update_person(person_id, name).unwrap();
|
||||
} else {
|
||||
let person = library.create_person(name).unwrap();
|
||||
self.emit_by_name::<()>("created", &[&person]);
|
||||
}
|
||||
|
||||
self.imp().navigation.get().unwrap().pop();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,9 +67,12 @@ mod imp {
|
|||
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![Signal::builder("person-selected")
|
||||
.param_types([Person::static_type()])
|
||||
.build()]
|
||||
vec![
|
||||
Signal::builder("person-selected")
|
||||
.param_types([Person::static_type()])
|
||||
.build(),
|
||||
Signal::builder("create").build(),
|
||||
]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
|
|
@ -113,6 +116,14 @@ impl MusicusPersonSelectorPopover {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn connect_create<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("create", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
f(&obj);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn search_changed(&self, entry: >k::SearchEntry) {
|
||||
self.search(&entry.text());
|
||||
|
|
@ -182,7 +193,7 @@ impl MusicusPersonSelectorPopover {
|
|||
}
|
||||
|
||||
fn create(&self) {
|
||||
log::info!("Create person!");
|
||||
self.emit_by_name::<()>("create", &[]);
|
||||
self.popdown();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,9 +67,12 @@ mod imp {
|
|||
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![Signal::builder("role-selected")
|
||||
.param_types([Role::static_type()])
|
||||
.build()]
|
||||
vec![
|
||||
Signal::builder("role-selected")
|
||||
.param_types([Role::static_type()])
|
||||
.build(),
|
||||
Signal::builder("create").build(),
|
||||
]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
|
|
@ -113,6 +116,14 @@ impl MusicusRoleSelectorPopover {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn connect_create<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("create", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
f(&obj);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn search_changed(&self, entry: >k::SearchEntry) {
|
||||
self.search(&entry.text());
|
||||
|
|
@ -182,7 +193,7 @@ impl MusicusRoleSelectorPopover {
|
|||
}
|
||||
|
||||
fn create(&self) {
|
||||
log::info!("Create role!");
|
||||
self.emit_by_name::<()>("create", &[]);
|
||||
self.popdown();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ impl MusicusTranslationEditor {
|
|||
self.add_entry(&util::LANG, &self.imp().entry_row.text());
|
||||
}
|
||||
|
||||
fn translation(&self) -> TranslatedString {
|
||||
pub fn translation(&self) -> TranslatedString {
|
||||
let imp = self.imp();
|
||||
let mut translation = HashMap::<String, String>::new();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
},
|
||||
editor::{
|
||||
instrument_selector_popover::MusicusInstrumentSelectorPopover,
|
||||
person_selector_popover::MusicusPersonSelectorPopover,
|
||||
person_editor::MusicusPersonEditor, person_selector_popover::MusicusPersonSelectorPopover,
|
||||
translation_editor::MusicusTranslationEditor,
|
||||
work_editor_composer_row::MusicusWorkEditorComposerRow,
|
||||
},
|
||||
|
|
@ -80,24 +80,20 @@ mod imp {
|
|||
let persons_popover = MusicusPersonSelectorPopover::new(self.library.get().unwrap());
|
||||
|
||||
let obj = self.obj().clone();
|
||||
persons_popover.connect_person_selected(
|
||||
move |_: &MusicusPersonSelectorPopover, person: Person| {
|
||||
let role = obj.library().composer_default_role().unwrap();
|
||||
let composer = Composer { person, role };
|
||||
let row = MusicusWorkEditorComposerRow::new(&obj.library(), composer);
|
||||
persons_popover.connect_person_selected(move |_, person| {
|
||||
obj.add_composer(person);
|
||||
});
|
||||
|
||||
row.connect_remove(clone!(@weak obj => move |row| {
|
||||
obj.imp().composer_list.remove(row);
|
||||
obj.imp().composer_rows.borrow_mut().retain(|c| c != row);
|
||||
}));
|
||||
let obj = self.obj().clone();
|
||||
persons_popover.connect_create(move |_| {
|
||||
let editor = MusicusPersonEditor::new(&obj.navigation(), &obj.library(), None);
|
||||
|
||||
obj.imp()
|
||||
.composer_list
|
||||
.insert(&row, obj.imp().composer_rows.borrow().len() as i32);
|
||||
editor.connect_created(clone!(@weak obj => move |_, person| {
|
||||
obj.add_composer(person);
|
||||
}));
|
||||
|
||||
obj.imp().composer_rows.borrow_mut().push(row);
|
||||
},
|
||||
);
|
||||
obj.navigation().push(&editor);
|
||||
});
|
||||
|
||||
self.select_person_box.append(&persons_popover);
|
||||
self.persons_popover.set(persons_popover).unwrap();
|
||||
|
|
@ -210,4 +206,21 @@ impl MusicusWorkEditor {
|
|||
fn add_instrument(&self, _: &adw::ActionRow) {
|
||||
self.imp().instruments_popover.get().unwrap().popup();
|
||||
}
|
||||
|
||||
fn add_composer(&self, person: Person) {
|
||||
let role = self.library().composer_default_role().unwrap();
|
||||
let composer = Composer { person, role };
|
||||
let row = MusicusWorkEditorComposerRow::new(&self.library(), composer);
|
||||
|
||||
row.connect_remove(clone!(@weak self as obj => move |row| {
|
||||
obj.imp().composer_list.remove(row);
|
||||
obj.imp().composer_rows.borrow_mut().retain(|c| c != row);
|
||||
}));
|
||||
|
||||
self.imp()
|
||||
.composer_list
|
||||
.insert(&row, self.imp().composer_rows.borrow().len() as i32);
|
||||
|
||||
self.imp().composer_rows.borrow_mut().push(row);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,8 +141,11 @@ impl MusicusHomePage {
|
|||
if let Some(tag) = self.imp().search_entry.tags().first() {
|
||||
match tag {
|
||||
Tag::Composer(person) | Tag::Performer(person) => {
|
||||
self.navigation()
|
||||
.push(&MusicusPersonEditor::new(Some(person)));
|
||||
self.navigation().push(&MusicusPersonEditor::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
Some(person),
|
||||
));
|
||||
}
|
||||
Tag::Ensemble(_) => todo!(),
|
||||
Tag::Work(work) => self.navigation().push(&MusicusWorkEditor::new(
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ use std::{
|
|||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use chrono::prelude::*;
|
||||
use diesel::{dsl::exists, prelude::*, QueryDsl, SqliteConnection};
|
||||
use gtk::{glib, glib::Properties, prelude::*, subclass::prelude::*};
|
||||
|
||||
use crate::db::{self, models::*, schema::*, tables};
|
||||
use crate::db::{self, models::*, schema::*, tables, TranslatedString};
|
||||
|
||||
diesel::sql_function! {
|
||||
/// Represents the SQL RANDOM() function.
|
||||
|
|
@ -444,6 +445,46 @@ impl MusicusLibrary {
|
|||
.filter(roles::role_id.eq("380d7e09eb2f49c1a90db2ba4acb6ffd"))
|
||||
.first::<Role>(connection)?)
|
||||
}
|
||||
|
||||
pub fn create_person(&self, name: TranslatedString) -> Result<Person> {
|
||||
let mut binding = self.imp().connection.borrow_mut();
|
||||
let connection = &mut *binding.as_mut().unwrap();
|
||||
|
||||
let now = Local::now().naive_local();
|
||||
|
||||
let person = Person {
|
||||
person_id: db::generate_id(),
|
||||
name,
|
||||
created_at: now,
|
||||
edited_at: now,
|
||||
last_used_at: now,
|
||||
last_played_at: None,
|
||||
};
|
||||
|
||||
diesel::insert_into(persons::table)
|
||||
.values(&person)
|
||||
.execute(connection)?;
|
||||
|
||||
Ok(person)
|
||||
}
|
||||
|
||||
pub fn update_person(&self, id: &str, name: TranslatedString) -> Result<()> {
|
||||
let mut binding = self.imp().connection.borrow_mut();
|
||||
let connection = &mut *binding.as_mut().unwrap();
|
||||
|
||||
let now = Local::now().naive_local();
|
||||
|
||||
diesel::update(persons::table)
|
||||
.filter(persons::person_id.eq(id))
|
||||
.set((
|
||||
persons::name.eq(name),
|
||||
persons::edited_at.eq(now),
|
||||
persons::last_used_at.eq(now),
|
||||
))
|
||||
.execute(connection)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue