
This is kinda a broken commit. There isn't any order to the adding of the slide anymore. So it'll need to find a way of keeping track of where all the current slides are and then insert the new ones in the correct order while moving the others around as well.
1288 lines
41 KiB
Rust
1288 lines
41 KiB
Rust
#[cxx_qt::bridge]
|
|
pub mod slide_model {
|
|
unsafe extern "C++" {
|
|
include!(< QAbstractListModel >);
|
|
include!("cxx-qt-lib/qhash.h");
|
|
type QHash_i32_QByteArray =
|
|
cxx_qt_lib::QHash<cxx_qt_lib::QHashPair_i32_QByteArray>;
|
|
include!("cxx-qt-lib/qmap.h");
|
|
type QMap_QString_QVariant =
|
|
cxx_qt_lib::QMap<cxx_qt_lib::QMapPair_QString_QVariant>;
|
|
include!("cxx-qt-lib/qvariant.h");
|
|
type QVariant = cxx_qt_lib::QVariant;
|
|
include!("cxx-qt-lib/qstring.h");
|
|
type QString = cxx_qt_lib::QString;
|
|
include!("cxx-qt-lib/qmodelindex.h");
|
|
type QModelIndex = cxx_qt_lib::QModelIndex;
|
|
include!("cxx-qt-lib/qvector.h");
|
|
type QVector_i32 = cxx_qt_lib::QVector<i32>;
|
|
include!("cxx-qt-lib/qlist.h");
|
|
type QList_QString = cxx_qt_lib::QList<QString>;
|
|
include!("cxx-qt-gen/slide_object.cxxqt.h");
|
|
type SlideObject = crate::slide_object::slide_object::SlideObject;
|
|
include!("cxx-qt-gen/song_model.cxxqt.h");
|
|
type SongModel = crate::songs::song_model::song_model::SongModel;
|
|
include!("cxx-qt-gen/video_model.cxxqt.h");
|
|
type VideoModel = crate::video_model::video_model::VideoModel;
|
|
include!("cxx-qt-gen/image_model.cxxqt.h");
|
|
type ImageModel = crate::image_model::image_model::ImageModel;
|
|
include!("cxx-qt-gen/presentation_model.cxxqt.h");
|
|
type PresentationModel = crate::presentation_model::presentation_model::PresentationModel;
|
|
}
|
|
|
|
#[qenum(SlideModel)]
|
|
enum SlideRoles {
|
|
Ty,
|
|
Text,
|
|
Audio,
|
|
ImageBackground,
|
|
VideoBackground,
|
|
HTextAlignment,
|
|
VTextAlignment,
|
|
Font,
|
|
FontSize,
|
|
ServiceItemId,
|
|
SlideIndex,
|
|
SlideCount,
|
|
Active,
|
|
Selected,
|
|
Looping,
|
|
VideoThumbnail,
|
|
VideoStartTime,
|
|
VideoEndTime,
|
|
Html,
|
|
ObsScene,
|
|
}
|
|
|
|
unsafe extern "RustQt" {
|
|
#[qobject]
|
|
#[base = "QAbstractListModel"]
|
|
#[qml_element]
|
|
#[qproperty(i32, count)]
|
|
#[qproperty(*mut SlideObject, slide_object)]
|
|
// #[qproperty(*mut SongModel, song_model)]
|
|
// #[qproperty(*mut VideoModel, video_model)]
|
|
// #[qproperty(*mut ImageModel, image_model)]
|
|
// #[qproperty(*mut PresentationModel, presentation_model)]
|
|
type SlideModel = super::SlideModelRust;
|
|
|
|
#[inherit]
|
|
#[qsignal]
|
|
fn data_changed(
|
|
self: Pin<&mut SlideModel>,
|
|
top_left: &QModelIndex,
|
|
bottom_right: &QModelIndex,
|
|
roles: &QVector_i32,
|
|
);
|
|
|
|
#[qsignal]
|
|
fn active_changed(self: Pin<&mut SlideModel>, index: &i32);
|
|
|
|
#[qinvokable]
|
|
fn add_video_thumbnail(
|
|
self: Pin<&mut SlideModel>,
|
|
index: i32,
|
|
) -> bool;
|
|
|
|
#[qinvokable]
|
|
fn clear(self: Pin<&mut SlideModel>);
|
|
|
|
#[qinvokable]
|
|
fn remove_item_from_service(
|
|
self: Pin<&mut SlideModel>,
|
|
index: i32,
|
|
_service_item: &QMap_QString_QVariant,
|
|
);
|
|
|
|
#[qinvokable]
|
|
fn remove_item(self: Pin<&mut SlideModel>, index: i32);
|
|
|
|
#[qinvokable]
|
|
fn insert_item_from_service(
|
|
self: Pin<&mut SlideModel>,
|
|
index: i32,
|
|
item_model_id: i32,
|
|
kind: &QString,
|
|
) -> Result<()>;
|
|
|
|
#[qinvokable]
|
|
fn add_item_from_service(
|
|
self: Pin<&mut SlideModel>,
|
|
item_model_id: i32,
|
|
kind: &QString,
|
|
) -> Result<()>;
|
|
|
|
#[qinvokable]
|
|
fn move_item_from_service(
|
|
self: Pin<&mut SlideModel>,
|
|
source_index: i32,
|
|
destination_index: i32,
|
|
_service_item: &QMap_QString_QVariant,
|
|
);
|
|
|
|
#[qinvokable]
|
|
fn get_item(
|
|
self: Pin<&mut SlideModel>,
|
|
index: i32,
|
|
) -> QMap_QString_QVariant;
|
|
|
|
#[qinvokable]
|
|
fn next(self: Pin<&mut SlideModel>) -> bool;
|
|
|
|
#[qinvokable]
|
|
fn prev(self: Pin<&mut SlideModel>) -> bool;
|
|
|
|
#[qinvokable]
|
|
fn get_slide_from_service(
|
|
self: Pin<&mut SlideModel>,
|
|
index: i32,
|
|
) -> i32;
|
|
|
|
#[qinvokable]
|
|
fn activate(self: Pin<&mut SlideModel>, index: i32) -> bool;
|
|
|
|
#[qinvokable]
|
|
fn update_obs_scene(
|
|
self: Pin<&mut SlideModel>,
|
|
index: i32,
|
|
obs_scene: QString,
|
|
);
|
|
}
|
|
|
|
impl cxx_qt::Threading for SlideModel {}
|
|
|
|
unsafe extern "RustQt" {
|
|
#[inherit]
|
|
unsafe fn begin_insert_rows(
|
|
self: Pin<&mut SlideModel>,
|
|
parent: &QModelIndex,
|
|
first: i32,
|
|
last: i32,
|
|
);
|
|
|
|
#[inherit]
|
|
unsafe fn end_insert_rows(self: Pin<&mut SlideModel>);
|
|
|
|
#[inherit]
|
|
unsafe fn begin_remove_rows(
|
|
self: Pin<&mut SlideModel>,
|
|
parent: &QModelIndex,
|
|
first: i32,
|
|
last: i32,
|
|
);
|
|
|
|
#[inherit]
|
|
unsafe fn end_remove_rows(self: Pin<&mut SlideModel>);
|
|
|
|
#[inherit]
|
|
unsafe fn begin_reset_model(self: Pin<&mut SlideModel>);
|
|
|
|
#[inherit]
|
|
unsafe fn end_reset_model(self: Pin<&mut SlideModel>);
|
|
|
|
#[inherit]
|
|
unsafe fn begin_move_rows(
|
|
self: Pin<&mut SlideModel>,
|
|
source_parent: &QModelIndex,
|
|
source_first: i32,
|
|
source_last: i32,
|
|
destination_parent: &QModelIndex,
|
|
destination_child: i32,
|
|
) -> bool;
|
|
|
|
#[inherit]
|
|
unsafe fn end_move_rows(self: Pin<&mut SlideModel>);
|
|
|
|
#[inherit]
|
|
fn can_fetch_more(
|
|
self: &SlideModel,
|
|
parent: &QModelIndex,
|
|
) -> bool;
|
|
|
|
#[inherit]
|
|
fn index(
|
|
self: &SlideModel,
|
|
row: i32,
|
|
column: i32,
|
|
parent: &QModelIndex,
|
|
) -> QModelIndex;
|
|
|
|
#[qinvokable]
|
|
#[cxx_override]
|
|
fn data(
|
|
self: &SlideModel,
|
|
index: &QModelIndex,
|
|
role: i32,
|
|
) -> QVariant;
|
|
|
|
#[qinvokable]
|
|
#[cxx_override]
|
|
fn role_names(self: &SlideModel) -> QHash_i32_QByteArray;
|
|
|
|
#[qinvokable]
|
|
#[cxx_override]
|
|
fn row_count(self: &SlideModel, _parent: &QModelIndex)
|
|
-> i32;
|
|
|
|
}
|
|
}
|
|
|
|
use crate::image_model::image_model::ImageModel;
|
|
use crate::image_model::{self, Image, ImageModelRust};
|
|
use crate::presentation_model::presentation_model::PresentationModel;
|
|
use crate::presentation_model::{self, Presentation, PresentationModelRust};
|
|
use crate::songs::song_model::song_model::{self, SongModel};
|
|
use crate::songs::song_model::{get_song, Song, SongModelRust};
|
|
use crate::video_model::video_model::VideoModel;
|
|
use crate::video_model::{self, Video, VideoModelRust};
|
|
use crate::{ffmpeg, slide_types::SlideType};
|
|
use crate::obs::Obs;
|
|
use crate::slide_model::slide_model::QList_QString;
|
|
use color_eyre::eyre::Result;
|
|
use color_eyre::Section;
|
|
use cxx_qt::{CxxQtType, Threading};
|
|
use cxx_qt_lib::{
|
|
CaseSensitivity, QByteArray, QList, QModelIndex, QString, QStringList, QVariant
|
|
};
|
|
use slide_model::SlideObject;
|
|
use std::error::Error;
|
|
use std::fmt::{Display};
|
|
use std::{path::PathBuf, pin::Pin};
|
|
use tracing::{debug, error};
|
|
|
|
use self::slide_model::{
|
|
QHash_i32_QByteArray, QMap_QString_QVariant, QVector_i32,
|
|
SlideRoles,
|
|
};
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct Slide {
|
|
pub text: String,
|
|
pub ty: SlideType,
|
|
pub audio: String,
|
|
pub image_background: String,
|
|
pub video_background: String,
|
|
pub htext_alignment: String,
|
|
pub vtext_alignment: String,
|
|
pub font: String,
|
|
pub font_size: i32,
|
|
pub slide_count: i32,
|
|
pub slide_index: i32,
|
|
pub service_item_id: i32,
|
|
pub active: bool,
|
|
pub selected: bool,
|
|
pub looping: bool,
|
|
pub video_thumbnail: String,
|
|
pub video_start_time: f32,
|
|
pub video_end_time: f32,
|
|
pub html: bool,
|
|
pub obs_scene: String,
|
|
}
|
|
|
|
impl Default for Slide {
|
|
fn default() -> Self {
|
|
Self {
|
|
text: String::default(),
|
|
ty: SlideType::Image,
|
|
audio: String::default(),
|
|
image_background: String::default(),
|
|
video_background: String::default(),
|
|
htext_alignment: String::default(),
|
|
vtext_alignment: String::default(),
|
|
font: String::default(),
|
|
font_size: 50,
|
|
slide_count: 1,
|
|
slide_index: 0,
|
|
service_item_id: 0,
|
|
active: false,
|
|
selected: false,
|
|
looping: false,
|
|
video_thumbnail: String::default(),
|
|
video_start_time: 0.0,
|
|
video_end_time: 0.0,
|
|
html: false,
|
|
obs_scene: String::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum ParseSlideError {
|
|
UnknownType,
|
|
}
|
|
|
|
impl Error for ParseSlideError {}
|
|
|
|
impl Display for ParseSlideError {
|
|
fn fmt(
|
|
&self,
|
|
f: &mut std::fmt::Formatter<'_>,
|
|
) -> std::fmt::Result {
|
|
let message = match self {
|
|
Self::UnknownType => "The type does not exist. It needs to be one of 'song', 'video', 'image', 'presentation', or 'content'",
|
|
};
|
|
write!(f, "Error: {message}")
|
|
}
|
|
}
|
|
|
|
impl TryFrom<Video> for Slide {
|
|
type Error = ParseSlideError;
|
|
fn try_from(value: Video) -> Result<Self, Self::Error> {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
impl Slide {
|
|
fn slides_from_song(song: Song) -> Result<Vec<Self>> {
|
|
let list = song.get_lyric_list();
|
|
let total = list.len();
|
|
let mut vector: Vec<Slide> = vec![];
|
|
list.iter().map(|t| t.to_string()).enumerate()
|
|
.for_each(|(i, s)| {
|
|
if song.background_type == "image" {
|
|
vector.push(
|
|
Self {
|
|
text: s,
|
|
ty: SlideType::Song,
|
|
audio: song.audio.clone(),
|
|
image_background: song.background.clone(),
|
|
video_background: "".to_owned(),
|
|
htext_alignment: song.horizontal_text_alignment.clone(),
|
|
vtext_alignment: song.vertical_text_alignment.clone(),
|
|
font: song.font.clone(),
|
|
font_size: song.font_size,
|
|
slide_count: total as i32,
|
|
slide_index: i as i32,
|
|
..Default::default()
|
|
}
|
|
);
|
|
} else {
|
|
vector.push(
|
|
Self {
|
|
text: s,
|
|
ty: SlideType::Song,
|
|
audio: song.audio.clone(),
|
|
image_background: "".to_owned(),
|
|
video_background: song.background.clone(),
|
|
htext_alignment: song.horizontal_text_alignment.clone(),
|
|
vtext_alignment: song.vertical_text_alignment.clone(),
|
|
font: song.font.clone(),
|
|
font_size: song.font_size,
|
|
slide_count: total as i32,
|
|
slide_index: i as i32,
|
|
..Default::default()
|
|
}
|
|
)
|
|
}
|
|
});
|
|
Ok(vector)
|
|
}
|
|
|
|
fn slide_from_video(video: Video) -> Result<Self> {
|
|
Ok(Self {
|
|
video_background: video.path,
|
|
video_start_time: video.start_time,
|
|
video_end_time: video.end_time,
|
|
looping: video.looping,
|
|
..Default::default()
|
|
})
|
|
}
|
|
|
|
fn slide_from_image(image: Image) -> Result<Self> {
|
|
Ok(Self {
|
|
image_background: image.path,
|
|
..Default::default()
|
|
})
|
|
}
|
|
|
|
fn slides_from_presentation(presentation: Presentation) -> Result<Vec<Self>> {
|
|
let total = presentation.page_count;
|
|
let mut slides: Vec<Slide> = vec![];
|
|
for i in 0..total {
|
|
slides.push(Self {
|
|
html: presentation.html,
|
|
slide_count: total,
|
|
slide_index: i,
|
|
image_background: presentation.path.clone(),
|
|
..Default::default()
|
|
})
|
|
};
|
|
Ok(slides)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct SlideModelRust {
|
|
current_index: i32,
|
|
slides: Vec<Slide>,
|
|
obs: Option<Obs>,
|
|
count: i32,
|
|
slide_object: *mut SlideObject,
|
|
// song_model: *mut SongModel,
|
|
// video_model: *mut VideoModel,
|
|
// image_model: *mut ImageModel,
|
|
// presentation_model: *mut PresentationModel,
|
|
}
|
|
|
|
impl Default for SlideModelRust {
|
|
fn default() -> Self {
|
|
let obs =
|
|
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
|
match Obs::new().await {
|
|
Ok(o) => Some(o),
|
|
Err(e) => {
|
|
error!(e);
|
|
None
|
|
}
|
|
}
|
|
});
|
|
Self {
|
|
current_index: 0,
|
|
slides: Vec::new(),
|
|
obs,
|
|
count: 0,
|
|
slide_object: std::ptr::null_mut(),
|
|
// song_model: std::ptr::null_mut(),
|
|
// video_model: std::ptr::null_mut(),
|
|
// image_model: std::ptr::null_mut(),
|
|
// presentation_model: std::ptr::null_mut(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl slide_model::SlideModel {
|
|
pub fn add_video_thumbnail(
|
|
mut self: Pin<&mut Self>,
|
|
index: i32,
|
|
) -> bool {
|
|
let mut vector_roles = QVector_i32::default();
|
|
vector_roles.append(self.get_role(SlideRoles::VideoThumbnail));
|
|
vector_roles.append(self.get_role(SlideRoles::VideoBackground));
|
|
vector_roles.append(self.get_role(SlideRoles::ImageBackground));
|
|
let rc = self.as_ref().count() - 1;
|
|
let tl = self.as_ref().index(0, 0, &QModelIndex::default());
|
|
let br = self.as_ref().index(rc, 0, &QModelIndex::default());
|
|
let thread = self.qt_thread();
|
|
if let Some(slide) =
|
|
self.as_mut().rust_mut().slides.get_mut(index as usize)
|
|
{
|
|
if !slide.video_background.is_empty() {
|
|
let path =
|
|
PathBuf::from(slide.video_background.to_string());
|
|
let screenshot = ffmpeg::bg_path_from_video(&path);
|
|
let mut screenshot_string = screenshot.clone().into_os_string().into_string().unwrap_or_default();
|
|
screenshot_string.insert_str(0, "file://");
|
|
slide.video_thumbnail = screenshot_string;
|
|
std::thread::spawn(move || {
|
|
match ffmpeg::bg_from_video(&path, &screenshot) {
|
|
Ok(_o) => {
|
|
debug!("Success making video background!")
|
|
}
|
|
Err(error) => error!(
|
|
?error,
|
|
"Error making video background"
|
|
),
|
|
};
|
|
|
|
match thread.queue(move |mut slide_model| {
|
|
slide_model.as_mut().data_changed(
|
|
&tl,
|
|
&br,
|
|
&vector_roles,
|
|
)
|
|
}) {
|
|
Ok(_o) => {
|
|
debug!("Success in creating qt_thread")
|
|
}
|
|
Err(error) => error!(
|
|
?error,
|
|
"Error in creating qt_thread"
|
|
),
|
|
}
|
|
});
|
|
}
|
|
}
|
|
true
|
|
}
|
|
|
|
pub fn clear(mut self: Pin<&mut Self>) {
|
|
debug!("CLEARING ALL SLIDES");
|
|
unsafe {
|
|
self.as_mut().begin_reset_model();
|
|
self.as_mut().rust_mut().slides.clear();
|
|
self.as_mut().end_reset_model();
|
|
}
|
|
}
|
|
|
|
pub fn remove_item_from_service(
|
|
mut self: Pin<&mut Self>,
|
|
index: i32,
|
|
_service_item: &QMap_QString_QVariant,
|
|
) {
|
|
debug!("Rusty-Removal-Time: {:?}", index);
|
|
let slides = self.slides.clone();
|
|
let slides_iter = slides.iter();
|
|
for (i, slide) in slides_iter.enumerate().rev() {
|
|
if slide.service_item_id == index {
|
|
self.as_mut().remove_item(i as i32);
|
|
debug!("Removing-slide: {:?}", i);
|
|
} else if slide.service_item_id > index {
|
|
if let Some(slide) =
|
|
self.as_mut().rust_mut().slides.get_mut(i)
|
|
{
|
|
debug!("changing-serviceid-of: {:?}", i);
|
|
debug!(
|
|
"changing-serviceid-fromandto: {:?}-{:?}",
|
|
slide.service_item_id,
|
|
slide.service_item_id - 1
|
|
);
|
|
slide.service_item_id -= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn remove_item(mut self: Pin<&mut Self>, index: i32) {
|
|
if index < 0 || (index as usize) >= self.slides.len() {
|
|
return;
|
|
}
|
|
|
|
unsafe {
|
|
self.as_mut().begin_remove_rows(
|
|
&QModelIndex::default(),
|
|
index,
|
|
index,
|
|
);
|
|
self.as_mut().rust_mut().slides.remove(index as usize);
|
|
self.as_mut().end_remove_rows();
|
|
}
|
|
debug!("removed-row: {:?}", index);
|
|
}
|
|
|
|
fn add_slide(mut self: Pin<&mut Self>, slide: &Slide) {
|
|
let index = self.as_ref().slides.len() as i32;
|
|
debug!("{:?}", slide);
|
|
let slide = slide.clone();
|
|
|
|
let count = self.as_ref().count;
|
|
self.as_mut().set_count(count + 1);
|
|
|
|
unsafe {
|
|
self.as_mut().begin_insert_rows(
|
|
&QModelIndex::default(),
|
|
index,
|
|
index,
|
|
);
|
|
self.as_mut().rust_mut().slides.push(slide);
|
|
self.as_mut().end_insert_rows();
|
|
}
|
|
self.as_mut().add_video_thumbnail(index);
|
|
}
|
|
|
|
fn insert_slide(
|
|
mut self: Pin<&mut Self>,
|
|
slide: &Slide,
|
|
index: i32,
|
|
) {
|
|
let slide = slide.clone();
|
|
// slide.slide_index = index;
|
|
debug!(?slide);
|
|
|
|
unsafe {
|
|
self.as_mut().begin_insert_rows(
|
|
&QModelIndex::default(),
|
|
index,
|
|
index,
|
|
);
|
|
self.as_mut()
|
|
.rust_mut()
|
|
.slides
|
|
.insert(index as usize, slide);
|
|
self.as_mut().end_insert_rows();
|
|
}
|
|
let count = self.as_ref().count;
|
|
self.as_mut().set_count(count + 1);
|
|
self.as_mut().add_video_thumbnail(index);
|
|
}
|
|
|
|
pub fn insert_item_from_service(
|
|
mut self: Pin<&mut Self>,
|
|
index: i32,
|
|
item_model_id: i32,
|
|
kind: &QString,
|
|
) -> Result<()> {
|
|
let kind = SlideType::try_from(kind.clone())?;
|
|
match kind {
|
|
SlideType::Song => {
|
|
let song = get_song(item_model_id)?;
|
|
let slides = Slide::slides_from_song(song)?;
|
|
slides.iter().for_each(|slide| self.as_mut().insert_slide(slide, index));
|
|
Ok(())
|
|
},
|
|
SlideType::Video => {
|
|
let video = video_model::get_video(item_model_id)?;
|
|
self.insert_slide(&Slide::slide_from_video(video)?, index);
|
|
Ok(())
|
|
},
|
|
SlideType::Image => {
|
|
let result = image_model::get_image(item_model_id);
|
|
match result {
|
|
Ok(image) => self.insert_slide(&Slide::slide_from_image(image)?, index),
|
|
Err(e) => {
|
|
e.with_note(|| {
|
|
format!("This might fail if we are loading the items from a file")
|
|
});
|
|
let mut slide = Slide::default();
|
|
slide.image_background = "qrc:/assets/black.jpg".to_owned();
|
|
self.insert_slide(&slide, index);
|
|
},
|
|
}
|
|
Ok(())
|
|
},
|
|
SlideType::Content => {
|
|
todo!();
|
|
Ok(())
|
|
},
|
|
SlideType::Presentation(_) => {
|
|
let presentation = presentation_model::get_presentation(item_model_id)?;
|
|
let slides = Slide::slides_from_presentation(presentation)?;
|
|
slides.iter().for_each(|slide| self.as_mut().insert_slide(slide, slide.slide_index + index));
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn add_item_from_service(
|
|
self: Pin<&mut Self>,
|
|
item_model_id: i32,
|
|
kind: &QString,
|
|
) -> Result<()>{
|
|
let index = self.count;
|
|
self.insert_item_from_service(index, item_model_id, kind)
|
|
}
|
|
|
|
pub fn move_item_from_service(
|
|
mut self: Pin<&mut Self>,
|
|
source_index: i32,
|
|
destination_index: i32,
|
|
_service_item: &QMap_QString_QVariant,
|
|
) {
|
|
if source_index == destination_index {
|
|
return;
|
|
}
|
|
|
|
debug!(source_index, destination_index);
|
|
|
|
let move_down = source_index < destination_index;
|
|
let slides = self.slides.clone();
|
|
let slides_iter = slides.iter();
|
|
|
|
let mut first_slide = 0;
|
|
let mut dest_slide = 0;
|
|
let mut count = 0;
|
|
let mut dest_count = 0;
|
|
|
|
// service item 1 moves to service item 2
|
|
// 1 becomes 2 and 2 becomes 1
|
|
|
|
// first slide is 1
|
|
// dest slide is 2
|
|
|
|
//lets get the first slide and count
|
|
|
|
if let Some((i, slide)) = slides_iter
|
|
.clone()
|
|
.enumerate().find(|slide| slide.1.service_item_id == source_index)
|
|
{
|
|
debug!(index = i, ?slide);
|
|
first_slide = i as i32;
|
|
count = if slide.slide_count == 0 {
|
|
1
|
|
} else {
|
|
slide.slide_count
|
|
};
|
|
}
|
|
|
|
// lets get the dest_slide and count
|
|
if move_down {
|
|
if let Some((i, slide)) = slides_iter
|
|
.clone()
|
|
.enumerate()
|
|
.rev()
|
|
.find(|slide| {
|
|
slide.1.service_item_id == destination_index
|
|
})
|
|
{
|
|
dest_slide = i as i32;
|
|
dest_count = if slide.slide_count == 0 {
|
|
1
|
|
} else {
|
|
slide.slide_count
|
|
};
|
|
debug!(
|
|
"RUST_dest_slide: {:?} with {:?} slides",
|
|
dest_slide, dest_count
|
|
);
|
|
}
|
|
} else if let Some((i, slide)) = slides_iter
|
|
.enumerate()
|
|
.find(|slide| {
|
|
slide.1.service_item_id == destination_index
|
|
})
|
|
{
|
|
dest_slide = i as i32;
|
|
dest_count = if slide.slide_count == 0 {
|
|
1
|
|
} else {
|
|
slide.slide_count
|
|
};
|
|
debug!("RUST_dest_slide: {:?}", dest_slide);
|
|
}
|
|
|
|
debug!(count, first_slide, dest_slide);
|
|
|
|
let slides = self.slides.clone();
|
|
let slides_iter = slides.iter();
|
|
|
|
self.as_mut().move_items(
|
|
first_slide as usize,
|
|
dest_slide as usize,
|
|
count as usize,
|
|
);
|
|
|
|
// unsafe {
|
|
// self.as_mut().begin_reset_model();
|
|
// }
|
|
|
|
let _rc = self.as_ref().count() - 1;
|
|
let tl = &self.as_ref().index(
|
|
first_slide,
|
|
0,
|
|
&QModelIndex::default(),
|
|
);
|
|
let br = &self.as_ref().index(
|
|
dest_slide,
|
|
0,
|
|
&QModelIndex::default(),
|
|
);
|
|
let mut vector_roles = QVector_i32::default();
|
|
vector_roles.append(self.get_role(SlideRoles::ServiceItemId));
|
|
vector_roles
|
|
.append(self.get_role(SlideRoles::ImageBackground));
|
|
|
|
// Change the service_item_id of the moved slide
|
|
if count > 1 {
|
|
if move_down {
|
|
debug!("While moving down, change service items id of moved slide");
|
|
for (i, _slide) in slides_iter
|
|
.clone()
|
|
.enumerate()
|
|
.filter(|x| {
|
|
x.0 >= (first_slide + dest_count) as usize
|
|
})
|
|
.filter(|x| {
|
|
x.0 < (first_slide + dest_count + count)
|
|
as usize
|
|
})
|
|
{
|
|
if let Some(slide) =
|
|
self.as_mut().rust_mut().slides.get_mut(i)
|
|
{
|
|
debug!(
|
|
?slide,
|
|
"rust: these ones right here officer. from {:?} to {:?}",
|
|
slide.service_item_id, destination_index
|
|
);
|
|
slide.service_item_id = destination_index;
|
|
}
|
|
}
|
|
} else {
|
|
debug!("While moving up, change service items id of moved slide");
|
|
for (i, _slide) in slides_iter
|
|
.clone()
|
|
.enumerate()
|
|
.filter(|x| x.0 >= dest_slide as usize)
|
|
.filter(|x| x.0 < (dest_slide + count) as usize)
|
|
{
|
|
if let Some(slide) =
|
|
self.as_mut().rust_mut().slides.get_mut(i)
|
|
{
|
|
debug!(
|
|
?slide,
|
|
"rust: these ones right here officer. from {:?} to {:?}",
|
|
slide.service_item_id, destination_index
|
|
);
|
|
slide.service_item_id = destination_index;
|
|
}
|
|
}
|
|
}
|
|
} else if let Some(slide) = self
|
|
.as_mut()
|
|
.rust_mut()
|
|
.slides
|
|
.get_mut(dest_slide as usize)
|
|
{
|
|
debug!(
|
|
internal_slide_index = slide.slide_index,
|
|
service_item = slide.service_item_id,
|
|
destination_index,
|
|
"This is the slide who's service item needs changed"
|
|
);
|
|
slide.service_item_id = destination_index;
|
|
}
|
|
|
|
// Change the service_item_id of the shifted slides, not the moved service_item
|
|
if move_down {
|
|
debug!("While moving down, change service item id");
|
|
for (i, _slide) in slides_iter
|
|
.clone()
|
|
.enumerate()
|
|
.filter(|x| x.1.service_item_id <= destination_index)
|
|
.filter(|x| x.1.service_item_id >= source_index)
|
|
.filter(|x| x.0 <= (dest_slide - count) as usize)
|
|
{
|
|
if let Some(slide) =
|
|
self.as_mut().rust_mut().slides.get_mut(i)
|
|
{
|
|
debug!(
|
|
?slide,
|
|
old_service_id = slide.service_item_id,
|
|
new_service_id = slide.service_item_id - 1,
|
|
"rust-switching-service",
|
|
);
|
|
slide.service_item_id -= 1;
|
|
}
|
|
}
|
|
} else {
|
|
debug!("While moving up, change service item id");
|
|
for (i, _slide) in slides_iter
|
|
.clone()
|
|
.enumerate()
|
|
.filter(|x| x.0 >= (dest_slide + count) as usize)
|
|
.filter(|x| x.1.service_item_id >= destination_index)
|
|
.filter(|x| x.1.service_item_id <= source_index)
|
|
{
|
|
if let Some(slide) =
|
|
self.as_mut().rust_mut().slides.get_mut(i)
|
|
{
|
|
debug!(
|
|
?slide,
|
|
old_service_id = slide.service_item_id,
|
|
new_service_id = slide.service_item_id + 1,
|
|
"rust-switching-service",
|
|
);
|
|
slide.service_item_id += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
self.as_mut().data_changed(tl, br, &vector_roles);
|
|
|
|
// unsafe {
|
|
// self.as_mut().end_reset_model();
|
|
// }
|
|
|
|
debug!("rust-move: {first_slide} to {dest_slide} with {count} slides");
|
|
}
|
|
|
|
fn move_items(
|
|
mut self: Pin<&mut Self>,
|
|
source_index: usize,
|
|
dest_index: usize,
|
|
count: usize,
|
|
) {
|
|
debug!(source_index, dest_index, count);
|
|
if source_index == dest_index {
|
|
return;
|
|
};
|
|
let end_slide = source_index + count - 1;
|
|
debug!("rust-end-slide: {:?}", end_slide);
|
|
debug!("rust-dest-slide: {:?}", dest_index);
|
|
let model_index = self.index(
|
|
source_index as i32,
|
|
0,
|
|
&QModelIndex::default(),
|
|
);
|
|
let parent = model_index.parent();
|
|
let qt_dest_index = if source_index < dest_index {
|
|
(dest_index + 1) as i32
|
|
} else {
|
|
dest_index as i32
|
|
};
|
|
unsafe {
|
|
self.as_mut().begin_move_rows(
|
|
&parent,
|
|
source_index as i32,
|
|
(source_index + count - 1) as i32,
|
|
&parent,
|
|
qt_dest_index,
|
|
);
|
|
if source_index < dest_index {
|
|
let move_amount =
|
|
dest_index - source_index - count + 1;
|
|
// debug!("rust-move_amount: {:?}", move_amount);
|
|
self.as_mut().rust_mut().slides
|
|
[source_index..=dest_index]
|
|
.rotate_right(move_amount);
|
|
} else {
|
|
let move_amount = end_slide - dest_index - count + 1;
|
|
debug!("rust-move_amount: {:?}", move_amount);
|
|
self.as_mut().rust_mut().slides
|
|
[dest_index..=end_slide]
|
|
.rotate_left(move_amount);
|
|
}
|
|
self.as_mut().end_move_rows();
|
|
}
|
|
}
|
|
|
|
pub fn get_item(
|
|
self: Pin<&mut Self>,
|
|
index: i32,
|
|
) -> QMap_QString_QVariant {
|
|
debug!("{index}");
|
|
let mut qvariantmap = QMap_QString_QVariant::default();
|
|
let idx = self.index(index, 0, &QModelIndex::default());
|
|
if !idx.is_valid() {
|
|
return qvariantmap;
|
|
}
|
|
let rn = self.as_ref().role_names();
|
|
let rn_iter = rn.iter();
|
|
if let Some(_slide) = self.rust().slides.get(index as usize) {
|
|
for i in rn_iter {
|
|
qvariantmap.insert(
|
|
QString::from(&i.1.to_string()),
|
|
self.as_ref().data(&idx, *i.0),
|
|
);
|
|
}
|
|
};
|
|
qvariantmap
|
|
}
|
|
|
|
pub fn next(mut self: Pin<&mut Self>) -> bool {
|
|
if let Some(object) = unsafe { self.slide_object.as_mut() } {
|
|
let object = unsafe { Pin::new_unchecked(object) };
|
|
let id = self.as_ref().current_index + 1;
|
|
if let Some(slide) = self.rust().slides.get(id as usize) {
|
|
object.set_slide(slide);
|
|
self.as_mut().activate(id);
|
|
self.as_mut().rust_mut().current_index = id;
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub fn prev(mut self: Pin<&mut Self>) -> bool {
|
|
if let Some(object) = unsafe { self.slide_object.as_mut() } {
|
|
let object = unsafe { Pin::new_unchecked(object) };
|
|
let id = self.as_ref().current_index - 1;
|
|
if let Some(slide) = self.rust().slides.get(id as usize) {
|
|
object.set_slide(slide);
|
|
self.as_mut().activate(id);
|
|
self.as_mut().rust_mut().current_index = id;
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub fn get_slide_from_service(
|
|
self: Pin<&mut Self>,
|
|
index: i32,
|
|
) -> i32 {
|
|
let slides = self.slides.clone();
|
|
let slides_iter = slides.iter();
|
|
debug!(service_item = index, "Getting slide from this item");
|
|
let mut id = 0;
|
|
if let Some((i, slide)) = slides_iter
|
|
.enumerate().find(|(_i, slide)| slide.service_item_id == index)
|
|
{
|
|
debug!(slide_id = i, ?slide);
|
|
id = i as i32;
|
|
}
|
|
id
|
|
}
|
|
|
|
pub fn activate(mut self: Pin<&mut Self>, index: i32) -> bool {
|
|
let rc = self.as_ref().count() - 1;
|
|
let tl = &self.as_ref().index(0, 0, &QModelIndex::default());
|
|
let br = &self.as_ref().index(rc, 0, &QModelIndex::default());
|
|
let mut vector_roles = QVector_i32::default();
|
|
vector_roles.append(self.get_role(SlideRoles::Active));
|
|
for slide in self.as_mut().rust_mut().slides.iter_mut() {
|
|
// debug!("slide is deactivating {:?}", i);
|
|
slide.active = false;
|
|
}
|
|
|
|
if let Some(slide) =
|
|
self.as_mut().rust_mut().slides.get_mut(index as usize)
|
|
{
|
|
debug!(
|
|
slide = index,
|
|
service_item = slide.service_item_id,
|
|
obs = slide.obs_scene.to_string(),
|
|
"This slide is activating"
|
|
);
|
|
let obs_scene = slide.obs_scene.to_string();
|
|
slide.active = true;
|
|
self.as_mut().data_changed(tl, br, &vector_roles);
|
|
|
|
if let Some(obs) = self.as_ref().obs.clone() {
|
|
match obs.set_scene(obs_scene) {
|
|
Ok(()) => debug!("Successfully set scene"),
|
|
Err(e) => error!(e),
|
|
}
|
|
} else {
|
|
debug!("Obs isn't connected");
|
|
}
|
|
|
|
// We use this signal generated by our signals enum to tell QML that
|
|
// the active slide has changed which is used to reposition views.
|
|
self.as_mut().active_changed(&index);
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub fn update_obs_scene(
|
|
mut self: Pin<&mut Self>,
|
|
index: i32,
|
|
obs_scene: QString,
|
|
) {
|
|
let rc = self.as_ref().count() - 1;
|
|
let tl = &self.as_ref().index(0, 0, &QModelIndex::default());
|
|
let br = &self.as_ref().index(rc, 0, &QModelIndex::default());
|
|
let mut vector_roles = QVector_i32::default();
|
|
vector_roles.append(self.get_role(SlideRoles::ObsScene));
|
|
if let Some(slide) =
|
|
self.as_mut().rust_mut().slides.get_mut(index as usize)
|
|
{
|
|
slide.obs_scene = obs_scene.to_string();
|
|
self.as_mut().data_changed(tl, br, &vector_roles);
|
|
}
|
|
}
|
|
|
|
fn get_role(&self, role: SlideRoles) -> i32 {
|
|
match role {
|
|
SlideRoles::Text => 1,
|
|
SlideRoles::Active => 12,
|
|
SlideRoles::Selected => 13,
|
|
SlideRoles::Looping => 14,
|
|
SlideRoles::VideoThumbnail => 15,
|
|
SlideRoles::VideoStartTime => 16,
|
|
SlideRoles::VideoEndTime => 17,
|
|
SlideRoles::Html => 18,
|
|
SlideRoles::ObsScene => 19,
|
|
_ => 0,
|
|
}
|
|
}
|
|
|
|
fn get_indices(
|
|
self: Pin<&mut Self>,
|
|
item_id: i32,
|
|
role: SlideRoles,
|
|
) -> (usize, QModelIndex, QVector_i32) {
|
|
let mut vector_roles = QVector_i32::default();
|
|
vector_roles.append(self.as_ref().get_role(role));
|
|
if let Some(_slide) =
|
|
self.as_ref().slides.get(item_id as usize)
|
|
{
|
|
let model_index = self.as_ref().index(
|
|
item_id,
|
|
0,
|
|
&QModelIndex::default(),
|
|
);
|
|
(item_id as usize, model_index, vector_roles)
|
|
} else {
|
|
error!(item_id, "This item appears to be missing");
|
|
(0, QModelIndex::default(), vector_roles)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// QAbstractListModel implementation
|
|
impl slide_model::SlideModel {
|
|
pub fn data(&self, index: &QModelIndex, role: i32) -> QVariant {
|
|
let role = SlideRoles { repr: role };
|
|
if let Some(slide) = self.slides.get(index.row() as usize) {
|
|
return match role {
|
|
SlideRoles::Ty => QVariant::from(&QString::from(&slide.ty.to_string())),
|
|
SlideRoles::Text => QVariant::from(&QString::from(&slide.text)),
|
|
SlideRoles::Audio => QVariant::from(&QString::from(&slide.audio)),
|
|
SlideRoles::ImageBackground => {
|
|
QVariant::from(&QString::from(&slide.image_background))
|
|
}
|
|
SlideRoles::VideoBackground => {
|
|
QVariant::from(&QString::from(&slide.video_background))
|
|
}
|
|
SlideRoles::HTextAlignment => {
|
|
QVariant::from(&QString::from(&slide.htext_alignment))
|
|
}
|
|
SlideRoles::VTextAlignment => {
|
|
QVariant::from(&QString::from(&slide.vtext_alignment))
|
|
}
|
|
SlideRoles::Font => QVariant::from(&QString::from(&slide.font)),
|
|
SlideRoles::FontSize => {
|
|
QVariant::from(&slide.font_size)
|
|
}
|
|
SlideRoles::ServiceItemId => {
|
|
QVariant::from(&slide.service_item_id)
|
|
}
|
|
SlideRoles::SlideIndex => {
|
|
QVariant::from(&slide.slide_index)
|
|
}
|
|
SlideRoles::SlideCount => {
|
|
QVariant::from(&slide.slide_count)
|
|
}
|
|
SlideRoles::Active => QVariant::from(&slide.active),
|
|
SlideRoles::Selected => {
|
|
QVariant::from(&slide.selected)
|
|
}
|
|
SlideRoles::Looping => QVariant::from(&slide.looping),
|
|
SlideRoles::VideoThumbnail => {
|
|
QVariant::from(&QString::from(&slide.video_thumbnail))
|
|
}
|
|
SlideRoles::VideoStartTime => {
|
|
QVariant::from(&slide.video_start_time)
|
|
}
|
|
SlideRoles::VideoEndTime => {
|
|
QVariant::from(&slide.video_end_time)
|
|
}
|
|
SlideRoles::Html => QVariant::from(&slide.html),
|
|
SlideRoles::ObsScene => {
|
|
QVariant::from(&QString::from(&slide.obs_scene))
|
|
}
|
|
_ => QVariant::default(),
|
|
};
|
|
}
|
|
|
|
QVariant::default()
|
|
}
|
|
|
|
// // Example of overriding a C++ virtual method and calling the base class implementation.
|
|
// pub fn can_fetch_more(&self, parent: &QModelIndex) -> bool {
|
|
// self.base_can_fetch_more(parent)
|
|
// }
|
|
|
|
pub fn role_names(&self) -> QHash_i32_QByteArray {
|
|
let mut roles = QHash_i32_QByteArray::default();
|
|
roles.insert(SlideRoles::Ty.repr, QByteArray::from("type"));
|
|
roles.insert(SlideRoles::Text.repr, QByteArray::from("text"));
|
|
roles.insert(
|
|
SlideRoles::Audio.repr,
|
|
QByteArray::from("audio"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::ImageBackground.repr,
|
|
QByteArray::from("imageBackground"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::VideoBackground.repr,
|
|
QByteArray::from("videoBackground"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::HTextAlignment.repr,
|
|
QByteArray::from("hTextAlignment"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::VTextAlignment.repr,
|
|
QByteArray::from("vTextAlignment"),
|
|
);
|
|
roles.insert(SlideRoles::Font.repr, QByteArray::from("font"));
|
|
roles.insert(
|
|
SlideRoles::FontSize.repr,
|
|
QByteArray::from("fontSize"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::ServiceItemId.repr,
|
|
QByteArray::from("serviceItemId"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::SlideIndex.repr,
|
|
QByteArray::from("slideIndex"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::SlideCount.repr,
|
|
QByteArray::from("imageCount"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::Active.repr,
|
|
QByteArray::from("active"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::Selected.repr,
|
|
QByteArray::from("selected"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::Looping.repr,
|
|
QByteArray::from("looping"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::VideoThumbnail.repr,
|
|
QByteArray::from("videoThumbnail"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::VideoStartTime.repr,
|
|
QByteArray::from("videoStartTime"),
|
|
);
|
|
roles.insert(
|
|
SlideRoles::VideoEndTime.repr,
|
|
QByteArray::from("videoEndTime"),
|
|
);
|
|
roles.insert(SlideRoles::Html.repr, QByteArray::from("html"));
|
|
roles.insert(
|
|
SlideRoles::ObsScene.repr,
|
|
QByteArray::from("obsScene"),
|
|
);
|
|
roles
|
|
}
|
|
|
|
pub fn row_count(&self, _parent: &QModelIndex) -> i32 {
|
|
let cnt = self.rust().slides.len() as i32;
|
|
// debug!("row count is {cnt}");
|
|
cnt
|
|
}
|
|
}
|
|
|
|
/// Extracts the string from a qmap with QVariant<QStrings> in it.
|
|
fn extract_string(item: &QMap_QString_QVariant, key: &str) -> String {
|
|
item.get(&QString::from(key))
|
|
.unwrap_or(QVariant::from(&QString::default()))
|
|
.value_or_default::<QString>().to_string()
|
|
}
|
|
|
|
fn extract_value(item: &QMap_QString_QVariant, key: &str) -> i32 {
|
|
item.get(&QString::from(key))
|
|
.unwrap_or(QVariant::from(&0))
|
|
.value_or_default::<i32>()
|
|
}
|
|
|
|
fn extract_float(item: &QMap_QString_QVariant, key: &str) -> f32 {
|
|
item.get(&QString::from(key))
|
|
.unwrap_or(QVariant::from(&0.0))
|
|
.value_or_default::<f32>()
|
|
}
|
|
|
|
fn extract_bool(item: &QMap_QString_QVariant, key: &str) -> bool {
|
|
item.get(&QString::from(key))
|
|
.unwrap_or(QVariant::from(&false))
|
|
.value_or_default::<bool>()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
|
|
|
|
#[test]
|
|
pub fn test_obs_setting_scene() {
|
|
assert_eq!(true, true)
|
|
}
|
|
}
|