Remove work sections

This commit is contained in:
Elias Projahn 2022-02-09 21:43:20 +01:00
parent 3350c6be95
commit ee8399e93d
10 changed files with 24 additions and 332 deletions

View file

@ -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";

View file

@ -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

View file

@ -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,
);

View file

@ -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,
})
}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -15,4 +15,3 @@ pub use work::*;
mod performance;
mod work_part;
mod work_section;

View file

@ -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) => {
let part = this.parts.borrow()[index].clone();
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);
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);
}
}
}
});
}));
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())?;

View file

@ -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(&section.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()
}
}