From c70abf95949fb4311346c6b0f35b55ab53edc339 Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Sun, 14 Jul 2024 13:03:44 +0200 Subject: [PATCH] Add role editor --- data/ui/role_editor.blp | 42 ++++++++++++ src/editor/mod.rs | 3 +- src/editor/role_editor.rs | 110 ++++++++++++++++++++++++++++++++ src/editor/translation_entry.rs | 2 +- src/library.rs | 62 ++++++++++++++++++ 5 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 data/ui/role_editor.blp create mode 100644 src/editor/role_editor.rs diff --git a/data/ui/role_editor.blp b/data/ui/role_editor.blp new file mode 100644 index 0000000..69429fe --- /dev/null +++ b/data/ui/role_editor.blp @@ -0,0 +1,42 @@ +using Gtk 4.0; +using Adw 1; + +template $MusicusRoleEditor: Adw.NavigationPage { + title: _("Role"); + + Adw.ToolbarView { + [top] + Adw.HeaderBar header_bar {} + + Adw.Clamp { + Gtk.Box { + orientation: vertical; + + Gtk.Label { + label: _("Name"); + xalign: 0; + margin-top: 24; + + styles [ + "heading" + ] + } + + $MusicusTranslationEditor name_editor { + margin-top: 12; + } + + Gtk.Button save_button { + margin-top: 24; + label: _("Create role"); + clicked => $save() swapped; + + styles [ + "card", + "save" + ] + } + } + } + } +} diff --git a/src/editor/mod.rs b/src/editor/mod.rs index d1f88be..124637b 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -2,8 +2,9 @@ pub mod activatable_row; pub mod instrument_selector_popover; pub mod person_editor; pub mod person_selector_popover; +pub mod role_editor; pub mod role_selector_popover; -pub mod translation_entry; pub mod translation_editor; +pub mod translation_entry; pub mod work_editor; pub mod work_editor_composer_row; \ No newline at end of file diff --git a/src/editor/role_editor.rs b/src/editor/role_editor.rs new file mode 100644 index 0000000..6557388 --- /dev/null +++ b/src/editor/role_editor.rs @@ -0,0 +1,110 @@ +use std::cell::OnceCell; + +use adw::{prelude::*, subclass::prelude::*}; +use gettextrs::gettext; +use gtk::glib::{self, subclass::Signal}; +use once_cell::sync::Lazy; + +use crate::{ + db::models::Role, editor::translation_editor::MusicusTranslationEditor, library::MusicusLibrary, +}; + +mod imp { + use super::*; + + #[derive(Debug, Default, gtk::CompositeTemplate)] + #[template(file = "data/ui/role_editor.blp")] + pub struct MusicusRoleEditor { + pub navigation: OnceCell, + pub library: OnceCell, + pub role_id: OnceCell, + + #[template_child] + pub name_editor: TemplateChild, + #[template_child] + pub save_button: TemplateChild, + } + + #[glib::object_subclass] + impl ObjectSubclass for MusicusRoleEditor { + const NAME: &'static str = "MusicusRoleEditor"; + type Type = super::MusicusRoleEditor; + type ParentType = adw::NavigationPage; + + fn class_init(klass: &mut Self::Class) { + MusicusTranslationEditor::static_type(); + klass.bind_template(); + klass.bind_template_instance_callbacks(); + } + + fn instance_init(obj: &glib::subclass::InitializingObject) { + obj.init_template(); + } + } + + impl ObjectImpl for MusicusRoleEditor { + fn signals() -> &'static [Signal] { + static SIGNALS: Lazy> = Lazy::new(|| { + vec![Signal::builder("created") + .param_types([Role::static_type()]) + .build()] + }); + + SIGNALS.as_ref() + } + } + + impl WidgetImpl for MusicusRoleEditor {} + impl NavigationPageImpl for MusicusRoleEditor {} +} + +glib::wrapper! { + pub struct MusicusRoleEditor(ObjectSubclass) + @extends gtk::Widget, adw::NavigationPage; +} + +#[gtk::template_callbacks] +impl MusicusRoleEditor { + pub fn new( + navigation: &adw::NavigationView, + library: &MusicusLibrary, + role: Option<&Role>, + ) -> 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(role) = role { + obj.imp().save_button.set_label(&gettext("Save changes")); + obj.imp().role_id.set(role.role_id.clone()).unwrap(); + obj.imp().name_editor.set_translation(&role.name); + } + + obj + } + + pub fn connect_created(&self, f: F) -> glib::SignalHandlerId { + self.connect_local("created", true, move |values| { + let obj = values[0].get::().unwrap(); + let role = values[1].get::().unwrap(); + f(&obj, role); + 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(role_id) = self.imp().role_id.get() { + library.update_role(role_id, name).unwrap(); + } else { + let role = library.create_role(name).unwrap(); + self.emit_by_name::<()>("created", &[&role]); + } + + self.imp().navigation.get().unwrap().pop(); + } +} diff --git a/src/editor/translation_entry.rs b/src/editor/translation_entry.rs index df50956..86bcffa 100644 --- a/src/editor/translation_entry.rs +++ b/src/editor/translation_entry.rs @@ -78,7 +78,7 @@ impl MusicusTranslationEntry { } pub fn translation(&self) -> String { - self.imp().text().into() + self.text().to_string() } #[template_callback] diff --git a/src/library.rs b/src/library.rs index ffad06c..44b4c7b 100644 --- a/src/library.rs +++ b/src/library.rs @@ -549,6 +549,29 @@ impl MusicusLibrary { Ok(instruments) } + pub fn search_works(&self, composer: &Person, search: &str) -> Result> { + let search = format!("%{}%", search); + let mut binding = self.imp().connection.borrow_mut(); + let connection = &mut *binding.as_mut().unwrap(); + + let works: Vec = works::table + .inner_join(work_persons::table) + .filter( + works::name + .like(&search) + .and(work_persons::person_id.eq(&composer.person_id)), + ) + .limit(9) + .select(works::all_columns) + .distinct() + .load::(connection)? + .into_iter() + .map(|w| Work::from_table(w, connection)) + .collect::>>()?; + + Ok(works) + } + pub fn composer_default_role(&self) -> Result { let mut binding = self.imp().connection.borrow_mut(); let connection = &mut *binding.as_mut().unwrap(); @@ -597,6 +620,45 @@ impl MusicusLibrary { Ok(()) } + + pub fn create_role(&self, name: TranslatedString) -> Result { + let mut binding = self.imp().connection.borrow_mut(); + let connection = &mut *binding.as_mut().unwrap(); + + let now = Local::now().naive_local(); + + let role = Role { + role_id: db::generate_id(), + name, + created_at: now, + edited_at: now, + last_used_at: now, + }; + + diesel::insert_into(roles::table) + .values(&role) + .execute(connection)?; + + Ok(role) + } + + pub fn update_role(&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(roles::table) + .filter(roles::role_id.eq(id)) + .set(( + roles::name.eq(name), + roles::edited_at.eq(now), + roles::last_used_at.eq(now), + )) + .execute(connection)?; + + Ok(()) + } } #[derive(Default, Debug)]