This commit is contained in:
parent
32a28dfd11
commit
13d4c31058
2 changed files with 159 additions and 66 deletions
|
|
@ -109,6 +109,18 @@ impl VerseName {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(Vec<u8>, String)> for VerseName {
|
||||
type Error = miette::Error;
|
||||
|
||||
fn try_from(
|
||||
value: (Vec<u8>, String),
|
||||
) -> std::result::Result<Self, Self::Error> {
|
||||
let (data, mime) = value;
|
||||
debug!(?mime);
|
||||
ron::de::from_bytes(&data).into_diagnostic()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMimeTypes for VerseName {
|
||||
fn available(&self) -> std::borrow::Cow<'static, [String]> {
|
||||
Cow::from(vec!["application/verse".to_string()])
|
||||
|
|
@ -118,7 +130,8 @@ impl AsMimeTypes for VerseName {
|
|||
&self,
|
||||
mime_type: &str,
|
||||
) -> Option<std::borrow::Cow<'static, [u8]>> {
|
||||
None
|
||||
let ron = ron::ser::to_string(self).ok()?;
|
||||
Some(Cow::from(ron.into_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -219,11 +232,21 @@ impl FromRow<'_, SqliteRow> for Song {
|
|||
});
|
||||
};
|
||||
|
||||
let mut verses = vec![];
|
||||
let verse_order: String = {
|
||||
let str: &str = row.try_get(0)?;
|
||||
str.split(' ')
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect()
|
||||
let vo: &str = row.try_get(0)?;
|
||||
if let Ok(verse_order_vec) =
|
||||
ron::de::from_str::<Vec<VerseName>>(vo)
|
||||
{
|
||||
verses = verse_order_vec;
|
||||
vo.split(' ')
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect()
|
||||
} else {
|
||||
vo.split(' ')
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
|
|
@ -236,12 +259,7 @@ impl FromRow<'_, SqliteRow> for Song {
|
|||
let string: String = row.try_get(11)?;
|
||||
string
|
||||
})),
|
||||
verse_order: Some({
|
||||
let str: &str = row.try_get(0)?;
|
||||
str.split(' ')
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect()
|
||||
}),
|
||||
verse_order: Some(verse_order),
|
||||
background: {
|
||||
let string: String = row.try_get(7)?;
|
||||
Background::try_from(string).ok()
|
||||
|
|
@ -653,18 +671,9 @@ pub async fn update_song_in_db(
|
|||
) -> Result<()> {
|
||||
// self.update_item(item.clone(), index)?;
|
||||
|
||||
let verse_order = {
|
||||
if let Some(vo) = item.verse_order {
|
||||
vo.into_iter()
|
||||
.map(|mut s| {
|
||||
s.push(' ');
|
||||
s
|
||||
})
|
||||
.collect::<String>()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
debug!(?item);
|
||||
let verse_order =
|
||||
ron::ser::to_string(&item.verses).into_diagnostic()?;
|
||||
|
||||
let audio = item
|
||||
.audio
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ pub struct SongEditor {
|
|||
#[debug(skip)]
|
||||
stroke_color_model: ColorPickerModel,
|
||||
verses: Option<Vec<VerseEditor>>,
|
||||
hovered_verse_chip: Option<usize>,
|
||||
stroke_color_picker_open: bool,
|
||||
}
|
||||
|
||||
|
|
@ -113,6 +114,8 @@ pub enum Message {
|
|||
FontSelectorOpen(bool),
|
||||
EditVerseOrder,
|
||||
OpenStrokeColorPicker,
|
||||
ChipHovered(Option<usize>),
|
||||
ChipDropped((usize, Vec<u8>, String)),
|
||||
}
|
||||
|
||||
impl SongEditor {
|
||||
|
|
@ -189,6 +192,7 @@ impl SongEditor {
|
|||
stroke_color_picker_open: false,
|
||||
verses: None,
|
||||
editing_verse_order: false,
|
||||
hovered_verse_chip: None,
|
||||
}
|
||||
}
|
||||
pub fn update(&mut self, message: Message) -> Action {
|
||||
|
|
@ -412,6 +416,31 @@ impl SongEditor {
|
|||
Message::EditVerseOrder => {
|
||||
self.editing_verse_order = !self.editing_verse_order;
|
||||
}
|
||||
Message::ChipHovered(index) => {
|
||||
self.hovered_verse_chip = index;
|
||||
}
|
||||
Message::ChipDropped((index, data, mime)) => {
|
||||
self.hovered_verse_chip = None;
|
||||
match VerseName::try_from((data, mime)) {
|
||||
Ok(verse) => {
|
||||
if let Some(song) = self.song.as_mut() {
|
||||
if let Some(verses) = song.verses.as_mut()
|
||||
{
|
||||
verses.insert(index, verse);
|
||||
let song = song.clone();
|
||||
return self.update_song(song);
|
||||
} else {
|
||||
error!("No verses in this song?")
|
||||
}
|
||||
} else {
|
||||
error!("No song here?")
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!(?e, "Couldn't convert verse back")
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::None => (),
|
||||
}
|
||||
Action::None
|
||||
|
|
@ -502,6 +531,7 @@ impl SongEditor {
|
|||
|
||||
fn left_column(&self) -> Element<Message> {
|
||||
let cosmic::cosmic_theme::Spacing {
|
||||
space_xxs,
|
||||
space_s,
|
||||
space_m,
|
||||
space_l,
|
||||
|
|
@ -579,65 +609,95 @@ impl SongEditor {
|
|||
button::icon(if self.editing_verse_order {
|
||||
icon::from_name("arrow-up")
|
||||
} else {
|
||||
icon::from_name("arrow-down")
|
||||
icon::from_name("edit")
|
||||
})
|
||||
.on_press(Message::EditVerseOrder);
|
||||
|
||||
let verse_options = container(
|
||||
scrollable(row(verse_chips).spacing(space_s)).direction(
|
||||
let verse_options =
|
||||
container(scrollable(row(verse_chips)).direction(
|
||||
Direction::Horizontal(
|
||||
Scrollbar::new().spacing(space_s),
|
||||
),
|
||||
),
|
||||
)
|
||||
.padding(space_s)
|
||||
.width(Length::Fill)
|
||||
.class(theme::Container::Primary);
|
||||
))
|
||||
.padding(space_s)
|
||||
.width(Length::Fill)
|
||||
.class(theme::Container::Primary);
|
||||
|
||||
let verse_order_items: Vec<Element<Message>> =
|
||||
if let Some(song) = &self.song {
|
||||
if let Some(verses) = &song.verses {
|
||||
verses
|
||||
.iter()
|
||||
.map(|verse| {
|
||||
let verse = verse.clone();
|
||||
let chip = verse_chip(verse)
|
||||
.map(|_| Message::None);
|
||||
let verse_chip_wrapped =
|
||||
RcElementWrapper::<Message>::new(
|
||||
chip,
|
||||
);
|
||||
Element::from(
|
||||
dnd_destination(
|
||||
verse_chip_wrapped.clone(),
|
||||
vec!["application/verse".into()],
|
||||
)
|
||||
.on_enter(|x, y, mimes| {
|
||||
debug!(x, y, ?mimes);
|
||||
Message::None
|
||||
})
|
||||
.on_finish(
|
||||
|mime, data, action, x, y| {
|
||||
debug!(mime, ?data, ?action);
|
||||
Message::None
|
||||
},
|
||||
),
|
||||
let verse_order_items: Vec<Element<Message>> = if let Some(
|
||||
song,
|
||||
) =
|
||||
&self.song
|
||||
{
|
||||
if let Some(verses) = &song.verses {
|
||||
verses
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, verse)| {
|
||||
let verse = verse.clone();
|
||||
let mut chip =
|
||||
verse_chip(verse).map(|_| Message::None);
|
||||
if let Some(hovered_chip) =
|
||||
self.hovered_verse_chip
|
||||
{
|
||||
if index == hovered_chip {
|
||||
let phantom_chip = horizontal_space().width(60).height(19)
|
||||
.apply(container)
|
||||
.padding(
|
||||
Padding::new(space_xxs.into())
|
||||
.right(space_s)
|
||||
.left(space_s),
|
||||
)
|
||||
.class(theme::Container::Custom(Box::new(move |t| {
|
||||
container::Style::default()
|
||||
.background(ContainerBackground::Color(
|
||||
Color::from(t.cosmic().secondary.base).scale_alpha(0.5)
|
||||
))
|
||||
.border(Border::default().rounded(space_m).width(2))
|
||||
})));
|
||||
chip = row![
|
||||
phantom_chip,
|
||||
chip
|
||||
]
|
||||
.spacing(space_s)
|
||||
.into();
|
||||
}
|
||||
}
|
||||
let verse_chip_wrapped =
|
||||
RcElementWrapper::<Message>::new(chip);
|
||||
Element::from(
|
||||
dnd_destination(
|
||||
verse_chip_wrapped.clone(),
|
||||
vec!["application/verse".into()],
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
.on_enter(move |x, y, mimes| {
|
||||
debug!(x, y, ?mimes);
|
||||
Message::ChipHovered(Some(index))
|
||||
})
|
||||
.on_leave(move || {
|
||||
Message::ChipHovered(None)
|
||||
})
|
||||
.on_finish(
|
||||
move |mime, data, action, x, y| {
|
||||
debug!(mime, ?data, ?action);
|
||||
Message::ChipDropped((index, data, mime))
|
||||
},
|
||||
),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
}
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let verse_order = container(row![
|
||||
scrollable(row(verse_order_items).spacing(space_s))
|
||||
.direction(Direction::Horizontal(Scrollbar::new()))
|
||||
.spacing(space_s),
|
||||
horizontal_space(),
|
||||
verse_chips_edit_toggle
|
||||
verse_chips_edit_toggle.width(Length::Fixed(50.0))
|
||||
])
|
||||
.padding(space_s)
|
||||
.width(Length::Fill)
|
||||
|
|
@ -787,6 +847,26 @@ impl SongEditor {
|
|||
)
|
||||
.gap(10);
|
||||
|
||||
let bold_button = tooltip(
|
||||
button::icon(icon::from_name("format-text-bold"))
|
||||
.on_press(Message::None),
|
||||
"Bold",
|
||||
tooltip::Position::Bottom,
|
||||
);
|
||||
let italic_button = tooltip(
|
||||
button::icon(icon::from_name("format-text-italic"))
|
||||
.on_press(Message::None),
|
||||
"Italicize",
|
||||
tooltip::Position::Bottom,
|
||||
);
|
||||
|
||||
let underline_button = tooltip(
|
||||
button::icon(icon::from_name("format-text-underline"))
|
||||
.on_press(Message::None),
|
||||
"Underline",
|
||||
tooltip::Position::Bottom,
|
||||
);
|
||||
|
||||
let stroke_size_row = row![
|
||||
icon(
|
||||
icon::from_path("./res/text-outline.svg".into())
|
||||
|
|
@ -865,6 +945,10 @@ impl SongEditor {
|
|||
// text::body("Font Size:"),
|
||||
font_size,
|
||||
vertical_rule(1).height(space_l),
|
||||
bold_button,
|
||||
italic_button,
|
||||
underline_button,
|
||||
vertical_rule(1).height(space_l),
|
||||
stroke_size_selector,
|
||||
text::body("Stroke Color:"),
|
||||
stroke_color_button,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue