mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Remove work sections
This commit is contained in:
parent
3350c6be95
commit
ee8399e93d
10 changed files with 24 additions and 332 deletions
|
|
@ -5,7 +5,6 @@ DROP TABLE "instruments";
|
|||
DROP TABLE "works";
|
||||
DROP TABLE "instrumentations";
|
||||
DROP TABLE "work_parts";
|
||||
DROP TABLE "work_sections";
|
||||
DROP TABLE "ensembles";
|
||||
DROP TABLE "recordings";
|
||||
DROP TABLE "performances";
|
||||
|
|
|
|||
|
|
@ -28,13 +28,6 @@ CREATE TABLE "work_parts" (
|
|||
"title" TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "work_sections" (
|
||||
"id" BIGINT NOT NULL PRIMARY KEY,
|
||||
"work" TEXT NOT NULL REFERENCES "works"("id") ON DELETE CASCADE,
|
||||
"title" TEXT NOT NULL,
|
||||
"before_index" BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "ensembles" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"name" TEXT NOT NULL
|
||||
|
|
|
|||
|
|
@ -75,15 +75,6 @@ table! {
|
|||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
work_sections (id) {
|
||||
id -> BigInt,
|
||||
work -> Text,
|
||||
title -> Text,
|
||||
before_index -> BigInt,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
works (id) {
|
||||
id -> Text,
|
||||
|
|
@ -102,7 +93,6 @@ joinable!(recordings -> works (work));
|
|||
joinable!(tracks -> mediums (medium));
|
||||
joinable!(tracks -> recordings (recording));
|
||||
joinable!(work_parts -> works (work));
|
||||
joinable!(work_sections -> works (work));
|
||||
joinable!(works -> persons (composer));
|
||||
|
||||
allow_tables_to_appear_in_same_query!(
|
||||
|
|
@ -115,6 +105,5 @@ allow_tables_to_appear_in_same_query!(
|
|||
recordings,
|
||||
tracks,
|
||||
work_parts,
|
||||
work_sections,
|
||||
works,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use super::generate_id;
|
||||
use super::schema::{instrumentations, work_parts, work_sections, works};
|
||||
use super::schema::{instrumentations, work_parts, works};
|
||||
use super::{Database, Error, Instrument, Person, Result};
|
||||
use diesel::prelude::*;
|
||||
use diesel::{Insertable, Queryable};
|
||||
|
|
@ -43,28 +43,12 @@ struct WorkPartRow {
|
|||
pub title: String,
|
||||
}
|
||||
|
||||
/// Table row data for a work section.
|
||||
#[derive(Insertable, Queryable, Debug, Clone)]
|
||||
#[table_name = "work_sections"]
|
||||
struct WorkSectionRow {
|
||||
pub id: i64,
|
||||
pub work: String,
|
||||
pub title: String,
|
||||
pub before_index: i64,
|
||||
}
|
||||
/// A concrete work part that can be recorded.
|
||||
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
|
||||
pub struct WorkPart {
|
||||
pub title: String,
|
||||
}
|
||||
|
||||
/// A heading between work parts.
|
||||
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
|
||||
pub struct WorkSection {
|
||||
pub title: String,
|
||||
pub before_index: usize,
|
||||
}
|
||||
|
||||
/// A specific work by a composer.
|
||||
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
|
||||
pub struct Work {
|
||||
|
|
@ -73,7 +57,6 @@ pub struct Work {
|
|||
pub composer: Person,
|
||||
pub instruments: Vec<Instrument>,
|
||||
pub parts: Vec<WorkPart>,
|
||||
pub sections: Vec<WorkSection>,
|
||||
}
|
||||
|
||||
impl Work {
|
||||
|
|
@ -85,7 +68,6 @@ impl Work {
|
|||
composer,
|
||||
instruments: Vec::new(),
|
||||
parts: Vec::new(),
|
||||
sections: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +111,6 @@ impl Database {
|
|||
let Work {
|
||||
instruments,
|
||||
parts,
|
||||
sections,
|
||||
..
|
||||
} = work;
|
||||
|
||||
|
|
@ -158,19 +139,6 @@ impl Database {
|
|||
.execute(&self.connection)?;
|
||||
}
|
||||
|
||||
for section in sections {
|
||||
let row = WorkSectionRow {
|
||||
id: rand::random(),
|
||||
work: work_id.to_string(),
|
||||
title: section.title,
|
||||
before_index: section.before_index as i64,
|
||||
};
|
||||
|
||||
diesel::insert_into(work_sections::table)
|
||||
.values(row)
|
||||
.execute(&self.connection)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
|
@ -221,19 +189,6 @@ impl Database {
|
|||
});
|
||||
}
|
||||
|
||||
let mut sections: Vec<WorkSection> = Vec::new();
|
||||
|
||||
let section_rows = work_sections::table
|
||||
.filter(work_sections::work.eq(&row.id))
|
||||
.load::<WorkSectionRow>(&self.connection)?;
|
||||
|
||||
for section_row in section_rows {
|
||||
sections.push(WorkSection {
|
||||
title: section_row.title,
|
||||
before_index: section_row.before_index as usize,
|
||||
});
|
||||
}
|
||||
|
||||
let person_id = row.composer;
|
||||
let person = self
|
||||
.get_person(&person_id)?
|
||||
|
|
@ -245,7 +200,6 @@ impl Database {
|
|||
title: row.title,
|
||||
instruments,
|
||||
parts,
|
||||
sections,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,5 @@
|
|||
<file preprocess="xml-stripblanks">ui/track_set_editor.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/work_editor.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/work_part_editor.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/work_section_editor.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
|
|
|||
|
|
@ -146,12 +146,6 @@
|
|||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="add_section_button">
|
||||
<property name="has-frame">false</property>
|
||||
<property name="icon-name">folder-new-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="add_part_button">
|
||||
<property name="has-frame">false</property>
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk" version="4.0"/>
|
||||
<requires lib="libadwaita" version="1.0"/>
|
||||
<object class="GtkBox" id="widget">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="AdwHeaderBar">
|
||||
<property name="show-start-title-buttons">false</property>
|
||||
<property name="show-end-title-buttons">false</property>
|
||||
<property name="title-widget">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">Work section</property>
|
||||
<style>
|
||||
<class name="title"/>
|
||||
</style>
|
||||
</object>
|
||||
</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="back_button">
|
||||
<property name="icon-name">go-previous-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkButton" id="save_button">
|
||||
<property name="icon-name">object-select-symbolic</property>
|
||||
<style>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="info_bar">
|
||||
<property name="revealed">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="vexpand">true</property>
|
||||
<child>
|
||||
<object class="AdwClamp">
|
||||
<property name="margin-start">12</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="margin-top">18</property>
|
||||
<property name="margin-bottom">12</property>
|
||||
<property name="maximum-size">500</property>
|
||||
<property name="tightening-threshold">300</property>
|
||||
<child>
|
||||
<object class="GtkFrame">
|
||||
<property name="valign">start</property>
|
||||
<child>
|
||||
<object class="GtkListBox">
|
||||
<property name="selection-mode">none</property>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="focusable">False</property>
|
||||
<property name="title" translatable="yes">Title</property>
|
||||
<property name="activatable-widget">title_entry</property>
|
||||
<child>
|
||||
<object class="GtkEntry" id="title_entry">
|
||||
<property name="valign">center</property>
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
@ -15,4 +15,3 @@ pub use work::*;
|
|||
|
||||
mod performance;
|
||||
mod work_part;
|
||||
mod work_section;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use super::work_part::WorkPartEditor;
|
||||
use super::work_section::WorkSectionEditor;
|
||||
use crate::navigator::{NavigationHandle, Screen};
|
||||
use crate::selectors::{InstrumentSelector, PersonSelector};
|
||||
use crate::widgets::{List, Widget};
|
||||
|
|
@ -8,26 +7,10 @@ use anyhow::Result;
|
|||
use gettextrs::gettext;
|
||||
use glib::clone;
|
||||
use gtk_macros::get_widget;
|
||||
use musicus_backend::db::{generate_id, Instrument, Person, Work, WorkPart, WorkSection};
|
||||
use musicus_backend::db::{generate_id, Instrument, Person, Work, WorkPart};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Either a work part or a work section.
|
||||
#[derive(Clone)]
|
||||
enum PartOrSection {
|
||||
Part(WorkPart),
|
||||
Section(WorkSection),
|
||||
}
|
||||
|
||||
impl PartOrSection {
|
||||
pub fn get_title(&self) -> String {
|
||||
match self {
|
||||
PartOrSection::Part(part) => part.title.clone(),
|
||||
PartOrSection::Section(section) => section.title.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A widget for editing and creating works.
|
||||
pub struct WorkEditor {
|
||||
handle: NavigationHandle<Work>,
|
||||
|
|
@ -41,7 +24,7 @@ pub struct WorkEditor {
|
|||
id: String,
|
||||
composer: RefCell<Option<Person>>,
|
||||
instruments: RefCell<Vec<Instrument>>,
|
||||
structure: RefCell<Vec<PartOrSection>>,
|
||||
parts: RefCell<Vec<WorkPart>>,
|
||||
}
|
||||
|
||||
impl Screen<Option<Work>, Work> for WorkEditor {
|
||||
|
|
@ -62,7 +45,6 @@ impl Screen<Option<Work>, Work> for WorkEditor {
|
|||
get_widget!(builder, gtk::Button, add_instrument_button);
|
||||
get_widget!(builder, gtk::Frame, structure_frame);
|
||||
get_widget!(builder, gtk::Button, add_part_button);
|
||||
get_widget!(builder, gtk::Button, add_section_button);
|
||||
|
||||
let instrument_list = List::new();
|
||||
instrument_frame.set_child(Some(&instrument_list.widget));
|
||||
|
|
@ -74,18 +56,7 @@ impl Screen<Option<Work>, Work> for WorkEditor {
|
|||
let (id, composer, instruments, structure) = match work {
|
||||
Some(work) => {
|
||||
title_entry.set_text(&work.title);
|
||||
|
||||
let mut structure = Vec::new();
|
||||
|
||||
for part in work.parts {
|
||||
structure.push(PartOrSection::Part(part));
|
||||
}
|
||||
|
||||
for section in work.sections {
|
||||
structure.insert(section.before_index, PartOrSection::Section(section));
|
||||
}
|
||||
|
||||
(work.id, Some(work.composer), work.instruments, structure)
|
||||
(work.id, Some(work.composer), work.instruments, work.parts)
|
||||
}
|
||||
None => (generate_id(), None, Vec::new(), Vec::new()),
|
||||
};
|
||||
|
|
@ -102,7 +73,7 @@ impl Screen<Option<Work>, Work> for WorkEditor {
|
|||
part_list,
|
||||
composer: RefCell::new(composer),
|
||||
instruments: RefCell::new(instruments),
|
||||
structure: RefCell::new(structure),
|
||||
parts: RefCell::new(structure),
|
||||
});
|
||||
|
||||
// Connect signals and callbacks
|
||||
|
|
@ -178,14 +149,14 @@ impl Screen<Option<Work>, Work> for WorkEditor {
|
|||
}));
|
||||
|
||||
this.part_list.set_make_widget_cb(clone!(@weak this => @default-panic, move |index| {
|
||||
let pos = &this.structure.borrow()[index];
|
||||
let part = &this.parts.borrow()[index];
|
||||
|
||||
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
||||
delete_button.set_valign(gtk::Align::Center);
|
||||
|
||||
delete_button.connect_clicked(clone!(@weak this => move |_| {
|
||||
let length = {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
let mut structure = this.parts.borrow_mut();
|
||||
structure.remove(index);
|
||||
structure.len()
|
||||
};
|
||||
|
|
@ -198,57 +169,37 @@ impl Screen<Option<Work>, Work> for WorkEditor {
|
|||
|
||||
edit_button.connect_clicked(clone!(@weak this => move |_| {
|
||||
spawn!(@clone this, async move {
|
||||
let part_or_section = this.structure.borrow()[index].clone();
|
||||
match part_or_section {
|
||||
PartOrSection::Part(part) => {
|
||||
if let Some(part) = push!(this.handle, WorkPartEditor, Some(part)).await {
|
||||
let length = {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure[index] = PartOrSection::Part(part);
|
||||
structure.len()
|
||||
};
|
||||
let part = this.parts.borrow()[index].clone();
|
||||
if let Some(part) = push!(this.handle, WorkPartEditor, Some(part)).await {
|
||||
let length = {
|
||||
let mut structure = this.parts.borrow_mut();
|
||||
structure[index] = part;
|
||||
structure.len()
|
||||
};
|
||||
|
||||
this.part_list.update(length);
|
||||
}
|
||||
}
|
||||
PartOrSection::Section(section) => {
|
||||
if let Some(section) = push!(this.handle, WorkSectionEditor, Some(section)).await {
|
||||
let length = {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure[index] = PartOrSection::Section(section);
|
||||
structure.len()
|
||||
};
|
||||
|
||||
this.part_list.update(length);
|
||||
}
|
||||
}
|
||||
this.part_list.update(length);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
let row = adw::ActionRowBuilder::new()
|
||||
.focusable(false)
|
||||
.title(&pos.get_title())
|
||||
.title(&part.title)
|
||||
.activatable_widget(&edit_button)
|
||||
.build();
|
||||
|
||||
row.add_suffix(&delete_button);
|
||||
row.add_suffix(&edit_button);
|
||||
|
||||
if let PartOrSection::Part(_) = pos {
|
||||
// TODO: Replace with better solution to differentiate parts and sections.
|
||||
row.set_margin_start(12);
|
||||
}
|
||||
|
||||
row.upcast()
|
||||
}));
|
||||
|
||||
this.part_list
|
||||
.set_move_cb(clone!(@weak this => move |old_index, new_index| {
|
||||
let length = {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure.swap(old_index, new_index);
|
||||
structure.len()
|
||||
let mut parts = this.parts.borrow_mut();
|
||||
parts.swap(old_index, new_index);
|
||||
parts.len()
|
||||
};
|
||||
|
||||
this.part_list.update(length);
|
||||
|
|
@ -258,23 +209,9 @@ impl Screen<Option<Work>, Work> for WorkEditor {
|
|||
spawn!(@clone this, async move {
|
||||
if let Some(part) = push!(this.handle, WorkPartEditor, None).await {
|
||||
let length = {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure.push(PartOrSection::Part(part));
|
||||
structure.len()
|
||||
};
|
||||
|
||||
this.part_list.update(length);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
add_section_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
spawn!(@clone this, async move {
|
||||
if let Some(section) = push!(this.handle, WorkSectionEditor, None).await {
|
||||
let length = {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure.push(PartOrSection::Section(section));
|
||||
structure.len()
|
||||
let mut parts = this.parts.borrow_mut();
|
||||
parts.push(part);
|
||||
parts.len()
|
||||
};
|
||||
|
||||
this.part_list.update(length);
|
||||
|
|
@ -289,7 +226,7 @@ impl Screen<Option<Work>, Work> for WorkEditor {
|
|||
}
|
||||
|
||||
this.instrument_list.update(this.instruments.borrow().len());
|
||||
this.part_list.update(this.structure.borrow().len());
|
||||
this.part_list.update(this.parts.borrow().len());
|
||||
|
||||
this
|
||||
}
|
||||
|
|
@ -311,22 +248,6 @@ impl WorkEditor {
|
|||
|
||||
/// Save the work.
|
||||
fn save(self: &Rc<Self>) -> Result<Work> {
|
||||
let mut section_count: usize = 0;
|
||||
let mut parts = Vec::new();
|
||||
let mut sections = Vec::new();
|
||||
|
||||
for (index, pos) in self.structure.borrow().iter().enumerate() {
|
||||
match pos {
|
||||
PartOrSection::Part(part) => parts.push(part.clone()),
|
||||
PartOrSection::Section(section) => {
|
||||
let mut section = section.clone();
|
||||
section.before_index = index - section_count;
|
||||
sections.push(section);
|
||||
section_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let work = Work {
|
||||
id: self.id.clone(),
|
||||
title: self.title_entry.text().to_string(),
|
||||
|
|
@ -336,8 +257,7 @@ impl WorkEditor {
|
|||
.clone()
|
||||
.expect("Tried to create work without composer!"),
|
||||
instruments: self.instruments.borrow().clone(),
|
||||
parts,
|
||||
sections,
|
||||
parts: self.parts.borrow().clone(),
|
||||
};
|
||||
|
||||
self.handle.backend.db().update_work(work.clone())?;
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
use crate::navigator::{NavigationHandle, Screen};
|
||||
use crate::widgets::Widget;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk_macros::get_widget;
|
||||
use musicus_backend::db::WorkSection;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// A dialog for creating or editing a work section.
|
||||
pub struct WorkSectionEditor {
|
||||
handle: NavigationHandle<WorkSection>,
|
||||
widget: gtk::Box,
|
||||
save_button: gtk::Button,
|
||||
title_entry: gtk::Entry,
|
||||
}
|
||||
|
||||
impl Screen<Option<WorkSection>, WorkSection> for WorkSectionEditor {
|
||||
/// Create a new section editor and optionally initialize it.
|
||||
fn new(section: Option<WorkSection>, handle: NavigationHandle<WorkSection>) -> Rc<Self> {
|
||||
// Create UI
|
||||
|
||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/work_section_editor.ui");
|
||||
|
||||
get_widget!(builder, gtk::Box, widget);
|
||||
get_widget!(builder, gtk::Button, back_button);
|
||||
get_widget!(builder, gtk::Button, save_button);
|
||||
get_widget!(builder, gtk::Entry, title_entry);
|
||||
|
||||
if let Some(section) = section {
|
||||
title_entry.set_text(§ion.title);
|
||||
}
|
||||
|
||||
let this = Rc::new(Self {
|
||||
handle,
|
||||
widget,
|
||||
save_button,
|
||||
title_entry,
|
||||
});
|
||||
|
||||
// Connect signals and callbacks
|
||||
|
||||
back_button.connect_clicked(clone!(@weak this => move |_| {
|
||||
this.handle.pop(None);
|
||||
}));
|
||||
|
||||
this.save_button
|
||||
.connect_clicked(clone!(@weak this => move |_| {
|
||||
let section = WorkSection {
|
||||
before_index: 0,
|
||||
title: this.title_entry.text().to_string(),
|
||||
};
|
||||
|
||||
this.handle.pop(Some(section));
|
||||
}));
|
||||
|
||||
this.title_entry
|
||||
.connect_changed(clone!(@weak this => move |_| this.validate()));
|
||||
|
||||
this.validate();
|
||||
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl WorkSectionEditor {
|
||||
/// Validate inputs and enable/disable saving.
|
||||
fn validate(&self) {
|
||||
self.save_button
|
||||
.set_sensitive(!self.title_entry.text().is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for WorkSectionEditor {
|
||||
fn get_widget(&self) -> gtk::Widget {
|
||||
self.widget.clone().upcast()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue