diff --git a/src/db/mod.rs b/src/db/mod.rs index 97ddf6f..df0150c 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -38,6 +38,11 @@ pub fn connect(file_name: &str) -> Result { Ok(connection) } +/// Generate a random string suitable as an item ID. +pub fn generate_id() -> String { + uuid::Uuid::new_v4().simple().to_string() +} + /// A single translated string value. #[derive(Serialize, Deserialize, AsExpression, FromSqlRow, Clone, Default, Debug)] #[diesel(sql_type = Text)] diff --git a/src/db/models.rs b/src/db/models.rs index edfdbc3..d641d7d 100644 --- a/src/db/models.rs +++ b/src/db/models.rs @@ -21,7 +21,7 @@ pub struct Work { } // TODO: Handle part composers. -#[derive(Clone, Debug)] +#[derive(Default, Clone, Debug)] pub struct WorkPart { pub work_id: String, pub level: u8, @@ -113,6 +113,13 @@ impl PartialEq for Composer { } } +impl Eq for WorkPart {} +impl PartialEq for WorkPart { + fn eq(&self, other: &Self) -> bool { + self.work_id == other.work_id + } +} + impl Work { pub fn from_table(data: tables::Work, connection: &mut SqliteConnection) -> Result { fn visit_children( diff --git a/src/editor/work_editor.rs b/src/editor/work_editor.rs index d1eec75..fe07050 100644 --- a/src/editor/work_editor.rs +++ b/src/editor/work_editor.rs @@ -1,5 +1,8 @@ use crate::{ - db::models::{Composer, Instrument, Person}, + db::{ + self, + models::{Composer, Instrument, Person, WorkPart}, + }, editor::{ instrument_selector_popover::MusicusInstrumentSelectorPopover, person_selector_popover::MusicusPersonSelectorPopover, @@ -10,6 +13,7 @@ use crate::{ }; use adw::{prelude::*, subclass::prelude::*}; +use gettextrs::gettext; use gtk::glib::{self, clone, Properties}; use std::cell::{OnceCell, RefCell}; @@ -28,6 +32,7 @@ mod imp { // results when finishing the process of editing the work. The composer rows // handle all state related to the composer. pub composer_rows: RefCell>, + pub parts: RefCell>, pub instruments: RefCell>, pub persons_popover: OnceCell, @@ -38,6 +43,8 @@ mod imp { #[template_child] pub select_person_box: TemplateChild, #[template_child] + pub part_list: TemplateChild, + #[template_child] pub instrument_list: TemplateChild, #[template_child] pub select_instrument_box: TemplateChild, @@ -109,9 +116,7 @@ mod imp { remove_button.connect_clicked( clone!(@weak obj, @weak row, @strong instrument => move |_| { obj.imp().instrument_list.remove(&row); - let mut instruments = obj.imp().instruments.borrow_mut(); - let index = instruments.iter().position(|i| *i == instrument).unwrap(); - instruments.remove(index); + obj.imp().instruments.borrow_mut().retain(|i| *i != instrument); }), ); @@ -152,7 +157,35 @@ impl MusicusWorkEditor { #[template_callback] fn add_part(&self, _: &adw::ActionRow) { - todo!(); + let part = WorkPart { + work_id: db::generate_id(), + ..Default::default() + }; + + let row = adw::EntryRow::builder().title(gettext("Name")).build(); + + let remove_button = gtk::Button::builder() + .icon_name("user-trash-symbolic") + .valign(gtk::Align::Center) + .css_classes(["flat"]) + .build(); + + remove_button.connect_clicked( + clone!(@weak self as obj, @weak row, @strong part => move |_| { + obj.imp().part_list.remove(&row); + obj.imp().parts.borrow_mut().retain(|p| *p != part); + }), + ); + + row.add_suffix(&remove_button); + + self.imp() + .part_list + .insert(&row, self.imp().parts.borrow().len() as i32); + + row.grab_focus(); + + self.imp().parts.borrow_mut().push(part); } #[template_callback]