use std::num::NonZero;
use crate::common::Rational32;
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct ObjectHandle {
pub(crate) internal: aviutl2_sys::plugin2::OBJECT_HANDLE,
}
impl std::fmt::Debug for ObjectHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ObjectHandle").field(&self.internal).finish()
}
}
impl From<aviutl2_sys::plugin2::OBJECT_HANDLE> for ObjectHandle {
fn from(value: aviutl2_sys::plugin2::OBJECT_HANDLE) -> Self {
Self { internal: value }
}
}
impl From<ObjectHandle> for aviutl2_sys::plugin2::OBJECT_HANDLE {
fn from(value: ObjectHandle) -> Self {
value.internal
}
}
unsafe impl Send for ObjectHandle {}
unsafe impl Sync for ObjectHandle {}
#[derive(Debug, Clone, Copy)]
pub struct EditInfo {
pub width: usize,
pub height: usize,
pub fps: Rational32,
pub sample_rate: usize,
pub frame: usize,
pub layer: usize,
pub frame_max: usize,
pub layer_max: usize,
pub display_frame_start: usize,
pub display_layer_start: usize,
pub display_frame_num: usize,
pub display_layer_num: usize,
pub select_range_start: Option<usize>,
pub select_range_end: Option<usize>,
pub grid_bpm_tempo: f32,
pub grid_bpm_beat: usize,
pub grid_bpm_offset: f32,
pub scene_id: i32,
}
impl EditInfo {
pub unsafe fn from_raw(ptr: *const aviutl2_sys::plugin2::EDIT_INFO) -> Self {
let raw = unsafe { &*ptr };
Self {
width: raw.width as usize,
height: raw.height as usize,
fps: Rational32::new(raw.rate, raw.scale),
sample_rate: raw.sample_rate as usize,
frame: raw.frame as usize,
layer: raw.layer as usize,
frame_max: raw.frame_max as usize,
layer_max: raw.layer_max as usize,
display_frame_start: raw.display_frame_start as usize,
display_layer_start: raw.display_layer_start as usize,
display_frame_num: raw.display_frame_num as usize,
display_layer_num: raw.display_layer_num as usize,
select_range_start: (raw.select_range_start >= 0)
.then_some(raw.select_range_start as usize),
select_range_end: (raw.select_range_end >= 0).then_some(raw.select_range_end as usize),
grid_bpm_tempo: raw.grid_bpm_tempo,
grid_bpm_beat: raw.grid_bpm_beat as usize,
grid_bpm_offset: raw.grid_bpm_offset,
scene_id: raw.scene_id,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ObjectLayerFrame {
pub layer: usize,
pub start: usize,
pub end: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct LayerFrameData {
pub layer: usize,
pub frame: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct MediaInfo {
pub video_track_num: Option<NonZero<usize>>,
pub audio_track_num: Option<NonZero<usize>>,
pub total_time: f64,
pub width: usize,
pub height: usize,
}
#[derive(Debug, Clone, Copy)]
pub enum MediaFileSupportMode {
ExtensionOnly,
Strict,
}
#[derive(thiserror::Error, Debug)]
pub enum EditSectionError {
#[error("api call failed")]
ApiCallFailed,
#[error("object does not exist")]
ObjectDoesNotExist,
#[error("input utf-8 string contains null byte")]
InputCstrContainsNull(#[from] std::ffi::NulError),
#[error("input utf-16 string contains null byte")]
InputCwstrContainsNull(#[from] crate::common::NullByteError),
#[error("value is out of range")]
ValueOutOfRange(#[from] std::num::TryFromIntError),
#[error("api returned non-utf8 data")]
NonUtf8Data(#[from] std::str::Utf8Error),
#[cfg(feature = "aviutl2-alias")]
#[error("alias parse error: {0}")]
ParseFailed(#[from] aviutl2_alias::TableParseError),
}
#[derive(thiserror::Error, Debug)]
pub enum EditSectionParsedError<E: std::error::Error + Send + Sync + 'static> {
#[error(transparent)]
EditSectionError(#[from] EditSectionError),
#[error("value parse error: {0}")]
ParseError(E),
}
pub type EditSectionResult<T> = Result<T, EditSectionError>;
#[derive(Debug)]
pub struct ReadSection {
pub(crate) internal: *mut aviutl2_sys::plugin2::EDIT_SECTION,
}
#[derive(Debug)]
pub struct EditSection {
pub info: EditInfo,
pub(crate) internal: *mut aviutl2_sys::plugin2::EDIT_SECTION,
read_section: ReadSection,
}
impl std::ops::Deref for EditSection {
type Target = ReadSection;
fn deref(&self) -> &Self::Target {
&self.read_section
}
}
impl ReadSection {
pub unsafe fn from_raw(ptr: *mut aviutl2_sys::plugin2::EDIT_SECTION) -> Self {
Self { internal: ptr }
}
pub fn find_object_after(
&self,
layer: usize,
frame: usize,
) -> EditSectionResult<Option<ObjectHandle>> {
let object_handle =
unsafe { ((*self.internal).find_object)(layer.try_into()?, frame.try_into()?) };
if object_handle.is_null() {
Ok(None)
} else {
Ok(Some(ObjectHandle {
internal: object_handle,
}))
}
}
pub fn count_object_effect(
&self,
object: ObjectHandle,
effect: &str,
) -> EditSectionResult<usize> {
self.ensure_object_exists(object)?;
let c_effect = crate::common::CWString::new(effect)?;
let count =
unsafe { ((*self.internal).count_object_effect)(object.internal, c_effect.as_ptr()) };
Ok(count.try_into()?)
}
pub fn get_object_layer_frame(
&self,
object: ObjectHandle,
) -> EditSectionResult<ObjectLayerFrame> {
self.ensure_object_exists(object)?;
let object = unsafe { ((*self.internal).get_object_layer_frame)(object.internal) };
Ok(ObjectLayerFrame {
layer: object.layer.try_into()?,
start: object.start.try_into()?,
end: object.end.try_into()?,
})
}
pub fn get_object_alias(&self, object: ObjectHandle) -> EditSectionResult<String> {
self.ensure_object_exists(object)?;
let alias_ptr = unsafe { ((*self.internal).get_object_alias)(object.internal) };
if alias_ptr.is_null() {
return Err(EditSectionError::ApiCallFailed);
}
let c_str = unsafe { std::ffi::CStr::from_ptr(alias_ptr) };
let alias = c_str.to_str()?.to_owned();
Ok(alias)
}
pub fn get_object_name(&self, object: ObjectHandle) -> EditSectionResult<Option<String>> {
self.ensure_object_exists(object)?;
let name_ptr = unsafe { ((*self.internal).get_object_name)(object.internal) };
if name_ptr.is_null() {
return Ok(None);
}
Ok(Some(unsafe { crate::common::load_wide_string(name_ptr) }))
}
pub fn get_object_effect_item(
&self,
object: ObjectHandle,
effect_name: &str,
effect_index: usize,
item: &str,
) -> EditSectionResult<String> {
self.ensure_object_exists(object)?;
let c_effect_name = crate::common::CWString::new(&effect_key(effect_name, effect_index))?;
let c_item = crate::common::CWString::new(item)?;
let value_ptr = unsafe {
((*self.internal).get_object_item_value)(
object.internal,
c_effect_name.as_ptr(),
c_item.as_ptr(),
)
};
if value_ptr.is_null() {
return Err(EditSectionError::ApiCallFailed);
}
let c_str = unsafe { std::ffi::CStr::from_ptr(value_ptr) };
let value = c_str.to_str()?.to_owned();
Ok(value)
}
#[cfg(feature = "aviutl2-alias")]
pub fn get_object_effect_item_parsed<T: aviutl2_alias::FromTableValue>(
&self,
object: ObjectHandle,
effect_name: &str,
effect_index: usize,
item: &str,
) -> Result<T, EditSectionParsedError<<T as aviutl2_alias::FromTableValue>::Err>>
where
<T as aviutl2_alias::FromTableValue>::Err: std::error::Error + Sync + Send + 'static,
{
let value_str = self.get_object_effect_item(object, effect_name, effect_index, item)?;
T::from_table_value(&value_str).map_err(EditSectionParsedError::ParseError)
}
pub fn get_focused_object(&self) -> EditSectionResult<Option<ObjectHandle>> {
let object_handle = unsafe { ((*self.internal).get_focus_object)() };
if object_handle.is_null() {
Ok(None)
} else {
Ok(Some(ObjectHandle {
internal: object_handle,
}))
}
}
pub fn get_selected_objects(&self) -> EditSectionResult<Vec<ObjectHandle>> {
let mut handles = Vec::new();
let num_objects = unsafe { ((*self.internal).get_selected_object_num)() };
for i in 0..num_objects {
let object_handle = unsafe { ((*self.internal).get_selected_object)(i) };
if object_handle.is_null() {
return Err(EditSectionError::ApiCallFailed);
}
handles.push(ObjectHandle {
internal: object_handle,
});
}
Ok(handles)
}
pub fn is_support_media_file<P: AsRef<std::path::Path>>(
&self,
file_path: P,
mode: MediaFileSupportMode,
) -> EditSectionResult<bool> {
let c_file_path = crate::common::CWString::new(&file_path.as_ref().to_string_lossy())?;
let is_supported = unsafe {
match mode {
MediaFileSupportMode::ExtensionOnly => {
((*self.internal).is_support_media_file)(c_file_path.as_ptr(), false)
}
MediaFileSupportMode::Strict => {
((*self.internal).is_support_media_file)(c_file_path.as_ptr(), true)
}
}
};
Ok(is_supported)
}
pub fn get_media_info<P: AsRef<std::path::Path>>(
&self,
file_path: P,
) -> EditSectionResult<MediaInfo> {
let c_file_path = crate::common::CWString::new(&file_path.as_ref().to_string_lossy())?;
let mut media_info = std::mem::MaybeUninit::<aviutl2_sys::plugin2::MEDIA_INFO>::uninit();
let success = unsafe {
((*self.internal).get_media_info)(
c_file_path.as_ptr(),
media_info.as_mut_ptr(),
std::mem::size_of::<aviutl2_sys::plugin2::MEDIA_INFO>() as i32,
)
};
if !success {
return Err(EditSectionError::ApiCallFailed);
}
let media_info = unsafe { media_info.assume_init() };
Ok(MediaInfo {
video_track_num: NonZero::new(media_info.video_track_num.try_into()?),
audio_track_num: NonZero::new(media_info.audio_track_num.try_into()?),
total_time: media_info.total_time,
width: media_info.width.try_into()?,
height: media_info.height.try_into()?,
})
}
pub fn get_layer_name(&self, layer: usize) -> EditSectionResult<Option<String>> {
let name_ptr = unsafe { ((*self.internal).get_layer_name)(layer.try_into()?) };
if name_ptr.is_null() {
return Ok(None);
}
Ok(Some(unsafe { crate::common::load_wide_string(name_ptr) }))
}
pub fn get_scene_name(&self) -> EditSectionResult<String> {
let name_ptr = unsafe { ((*self.internal).get_scene_name)() };
if name_ptr.is_null() {
return Err(EditSectionError::ApiCallFailed);
}
Ok(unsafe { crate::common::load_wide_string(name_ptr) })
}
pub fn object_exists(&self, object: ObjectHandle) -> bool {
let object = unsafe { ((*self.internal).get_object_layer_frame)(object.internal) };
object.layer != -1
}
fn ensure_object_exists(&self, object: ObjectHandle) -> EditSectionResult<()> {
if !self.object_exists(object) {
return Err(EditSectionError::ObjectDoesNotExist);
}
Ok(())
}
pub fn objects_in_layer(
&self,
layer: usize,
) -> EditSectionLayerObjectsIterator<'_, ReadSection> {
EditSectionLayerObjectsIterator::new(self, layer)
}
pub fn layer<'a>(&'a self, layer: usize) -> EditSectionLayerCaller<'a, ReadSection> {
EditSectionLayerCaller::new(self, layer)
}
pub fn object<'a>(
&'a self,
object: &'a ObjectHandle,
) -> EditSectionObjectCaller<'a, ReadSection> {
EditSectionObjectCaller::new(self, object)
}
}
impl EditSection {
pub unsafe fn from_raw(ptr: *mut aviutl2_sys::plugin2::EDIT_SECTION) -> Self {
let info = unsafe { EditInfo::from_raw((*ptr).info) };
Self {
internal: ptr,
info,
read_section: ReadSection { internal: ptr },
}
}
pub fn create_object_from_alias(
&self,
alias: &str,
layer: usize,
frame: usize,
length: usize,
) -> EditSectionResult<ObjectHandle> {
let c_alias = std::ffi::CString::new(alias)?;
let object_handle = unsafe {
((*self.internal).create_object_from_alias)(
c_alias.as_ptr(),
layer.try_into()?,
frame.try_into()?,
length.try_into()?,
)
};
if object_handle.is_null() {
return Err(EditSectionError::ApiCallFailed);
}
Ok(ObjectHandle {
internal: object_handle,
})
}
pub fn set_object_name(
&self,
object: ObjectHandle,
name: Option<&str>,
) -> EditSectionResult<()> {
self.read_section.ensure_object_exists(object)?;
match name {
None => {
unsafe { ((*self.internal).set_object_name)(object.internal, std::ptr::null()) };
Ok(())
}
Some(name) => {
let c_name = crate::common::CWString::new(name)?;
unsafe {
((*self.internal).set_object_name)(object.internal, c_name.as_ptr());
}
Ok(())
}
}
}
pub fn set_object_effect_item(
&self,
object: ObjectHandle,
effect_name: &str,
effect_index: usize,
item: &str,
value: &str,
) -> EditSectionResult<()> {
self.read_section.ensure_object_exists(object)?;
let c_effect_name = crate::common::CWString::new(&effect_key(effect_name, effect_index))?;
let c_item = crate::common::CWString::new(item)?;
let c_value = std::ffi::CString::new(value)?;
let success = unsafe {
((*self.internal).set_object_item_value)(
object.internal,
c_effect_name.as_ptr(),
c_item.as_ptr(),
c_value.as_ptr(),
)
};
if !success {
return Err(EditSectionError::ApiCallFailed);
}
Ok(())
}
pub fn move_object(
&self,
object: ObjectHandle,
new_layer: usize,
new_start_frame: usize,
) -> EditSectionResult<()> {
self.read_section.ensure_object_exists(object)?;
let success = unsafe {
((*self.internal).move_object)(
object.internal,
new_layer.try_into()?,
new_start_frame.try_into()?,
)
};
if !success {
return Err(EditSectionError::ApiCallFailed);
}
Ok(())
}
pub fn delete_object(&self, object: ObjectHandle) -> EditSectionResult<()> {
self.read_section.ensure_object_exists(object)?;
unsafe { ((*self.internal).delete_object)(object.internal) };
Ok(())
}
pub fn focus_object(&self, object: ObjectHandle) -> EditSectionResult<()> {
self.read_section.ensure_object_exists(object)?;
unsafe { ((*self.internal).set_focus_object)(object.internal) };
Ok(())
}
pub fn get_project_file<'handle>(
&'handle self,
edit_handle: &crate::generic::EditHandle,
) -> crate::generic::ProjectFile<'handle> {
let pf_ptr = unsafe { ((*self.internal).get_project_file)(edit_handle.internal) };
unsafe { crate::generic::ProjectFile::from_raw(pf_ptr) }
}
pub fn get_mouse_layer_frame(&self) -> EditSectionResult<Option<LayerFrameData>> {
let mut layer = 0;
let mut frame = 0;
let on_layer_edit =
unsafe { ((*self.internal).get_mouse_layer_frame)(&mut layer, &mut frame) };
if on_layer_edit {
Ok(Some(LayerFrameData {
layer: layer.try_into()?,
frame: frame.try_into()?,
}))
} else {
Ok(None)
}
}
pub fn pos_to_layer_frame(&self, x: i32, y: i32) -> EditSectionResult<Option<LayerFrameData>> {
let mut layer = 0;
let mut frame = 0;
let on_layer_edit =
unsafe { ((*self.internal).pos_to_layer_frame)(x, y, &mut layer, &mut frame) };
if on_layer_edit {
Ok(Some(LayerFrameData {
layer: layer.try_into()?,
frame: frame.try_into()?,
}))
} else {
Ok(None)
}
}
pub fn create_object_from_media_file<P: AsRef<std::path::Path>>(
&self,
file_path: P,
layer: usize,
frame: usize,
length: Option<usize>,
) -> EditSectionResult<ObjectHandle> {
let c_file_path = crate::common::CWString::new(&file_path.as_ref().to_string_lossy())?;
let object_handle = unsafe {
((*self.internal).create_object_from_media_file)(
c_file_path.as_ptr(),
layer.try_into()?,
frame.try_into()?,
length.unwrap_or(0).try_into()?,
)
};
if object_handle.is_null() {
return Err(EditSectionError::ApiCallFailed);
}
Ok(ObjectHandle {
internal: object_handle,
})
}
pub fn create_object(
&self,
effect: &str,
layer: usize,
frame: usize,
length: Option<usize>,
) -> EditSectionResult<ObjectHandle> {
let c_effect = crate::common::CWString::new(effect)?;
let object_handle = unsafe {
((*self.internal).create_object)(
c_effect.as_ptr(),
layer.try_into()?,
frame.try_into()?,
length.unwrap_or(0).try_into()?,
)
};
if object_handle.is_null() {
return Err(EditSectionError::ApiCallFailed);
}
Ok(ObjectHandle {
internal: object_handle,
})
}
pub fn set_cursor_layer_frame(&self, layer: usize, frame: usize) -> EditSectionResult<()> {
unsafe {
((*self.internal).set_cursor_layer_frame)(layer.try_into()?, frame.try_into()?);
}
Ok(())
}
pub fn set_display_layer_frame(&self, layer: usize, frame: usize) -> EditSectionResult<()> {
unsafe {
((*self.internal).set_display_layer_frame)(layer.try_into()?, frame.try_into()?);
}
Ok(())
}
pub fn set_select_range(&self, start: usize, end: usize) -> EditSectionResult<()> {
unsafe {
((*self.internal).set_select_range)(start.try_into()?, end.try_into()?);
}
Ok(())
}
pub fn clear_select_range(&self) -> EditSectionResult<()> {
unsafe {
((*self.internal).set_select_range)(-1, -1);
}
Ok(())
}
pub fn set_grid_bpm(&self, tempo: f32, beat: usize, offset: f32) -> EditSectionResult<()> {
unsafe {
((*self.internal).set_grid_bpm)(tempo, beat.try_into()?, offset);
}
Ok(())
}
pub fn set_layer_name(&self, layer: usize, name: Option<&str>) -> EditSectionResult<()> {
match name {
None => {
unsafe { ((*self.internal).set_layer_name)(layer.try_into()?, std::ptr::null()) };
Ok(())
}
Some(name) => {
let c_name = crate::common::CWString::new(name)?;
unsafe {
((*self.internal).set_layer_name)(layer.try_into()?, c_name.as_ptr());
}
Ok(())
}
}
}
pub fn set_scene_name(&self, name: &str) -> EditSectionResult<()> {
let c_name = crate::common::CWString::new(name)?;
unsafe { ((*self.internal).set_scene_name)(c_name.as_ptr()) };
Ok(())
}
pub fn set_scene_size(&self, width: usize, height: usize) -> EditSectionResult<()> {
unsafe {
((*self.internal).set_scene_size)(width.try_into()?, height.try_into()?);
}
Ok(())
}
pub fn set_scene_fps(&self, fps: Rational32) -> EditSectionResult<()> {
unsafe {
((*self.internal).set_scene_frame_rate)(*fps.numer(), *fps.denom());
}
Ok(())
}
pub fn set_scene_sample_rate(&self, sample_rate: usize) -> EditSectionResult<()> {
unsafe {
((*self.internal).set_scene_sample_rate)(sample_rate.try_into()?);
}
Ok(())
}
pub fn layers(&self) -> EditSectionLayersIterator<'_> {
EditSectionLayersIterator::new(self)
}
pub fn objects_in_layer(
&self,
layer: usize,
) -> EditSectionLayerObjectsIterator<'_, EditSection> {
EditSectionLayerObjectsIterator::new(self, layer)
}
pub fn layer<'a>(&'a self, layer: usize) -> EditSectionLayerCaller<'a, EditSection> {
EditSectionLayerCaller::new(self, layer)
}
pub fn object<'a>(
&'a self,
object: &'a ObjectHandle,
) -> EditSectionObjectCaller<'a, EditSection> {
EditSectionObjectCaller::new(self, object)
}
}
trait ReadSectionProvider {
fn as_read_section(&self) -> &ReadSection;
}
impl ReadSectionProvider for ReadSection {
fn as_read_section(&self) -> &ReadSection {
self
}
}
impl ReadSectionProvider for EditSection {
fn as_read_section(&self) -> &ReadSection {
&self.read_section
}
}
pub struct EditSectionObjectCaller<'a, S> {
edit_section: &'a S,
pub handle: &'a ObjectHandle,
}
impl<'a, S> EditSectionObjectCaller<'a, S> {
pub fn new(edit_section: &'a S, object: &'a ObjectHandle) -> Self {
Self {
edit_section,
handle: object,
}
}
}
#[expect(private_bounds)]
impl<S> EditSectionObjectCaller<'_, S>
where
S: ReadSectionProvider,
{
fn read_section(&self) -> &ReadSection {
self.edit_section.as_read_section()
}
pub fn get_layer_frame(&self) -> EditSectionResult<ObjectLayerFrame> {
self.read_section().get_object_layer_frame(*self.handle)
}
pub fn get_alias(&self) -> EditSectionResult<String> {
self.read_section().get_object_alias(*self.handle)
}
#[cfg(feature = "aviutl2-alias")]
pub fn get_alias_parsed(&self) -> EditSectionResult<aviutl2_alias::Table> {
self.read_section()
.get_object_alias(*self.handle)?
.parse()
.map_err(Into::into)
}
pub fn count_effect(&self, effect: &str) -> EditSectionResult<usize> {
self.read_section()
.count_object_effect(*self.handle, effect)
}
pub fn get_effect_item(
&self,
effect_name: &str,
effect_index: usize,
item: &str,
) -> EditSectionResult<String> {
self.read_section()
.get_object_effect_item(*self.handle, effect_name, effect_index, item)
}
#[cfg(feature = "aviutl2-alias")]
pub fn get_effect_item_parsed<T: aviutl2_alias::FromTableValue>(
&self,
effect_name: &str,
effect_index: usize,
item: &str,
) -> Result<T, EditSectionParsedError<<T as aviutl2_alias::FromTableValue>::Err>>
where
<T as aviutl2_alias::FromTableValue>::Err: std::error::Error + Sync + Send + 'static,
{
let value_str = self.get_effect_item(effect_name, effect_index, item)?;
T::from_table_value(&value_str).map_err(EditSectionParsedError::ParseError)
}
pub fn exists(&self) -> bool {
self.read_section().object_exists(*self.handle)
}
pub fn get_name(&self) -> EditSectionResult<Option<String>> {
self.read_section().get_object_name(*self.handle)
}
}
impl EditSectionObjectCaller<'_, EditSection> {
pub fn set_effect_item(
&self,
effect_name: &str,
effect_index: usize,
item: &str,
value: &str,
) -> EditSectionResult<()> {
self.edit_section.set_object_effect_item(
*self.handle,
effect_name,
effect_index,
item,
value,
)
}
pub fn move_object(&self, new_layer: usize, new_start_frame: usize) -> EditSectionResult<()> {
self.edit_section
.move_object(*self.handle, new_layer, new_start_frame)
}
pub fn delete_object(&self) -> EditSectionResult<()> {
self.edit_section.delete_object(*self.handle)
}
pub fn focus_object(&self) -> EditSectionResult<()> {
self.edit_section.focus_object(*self.handle)
}
pub fn set_name(&self, name: Option<&str>) -> EditSectionResult<()> {
self.edit_section.set_object_name(*self.handle, name)
}
}
pub struct EditSectionLayerCaller<'a, S> {
edit_section: &'a S,
pub index: usize,
}
impl<'a, S> EditSectionLayerCaller<'a, S> {
pub fn new(edit_section: &'a S, layer: usize) -> Self {
Self {
edit_section,
index: layer,
}
}
}
#[expect(private_bounds)]
impl<S> EditSectionLayerCaller<'_, S>
where
S: ReadSectionProvider,
{
fn read_section(&self) -> &ReadSection {
self.edit_section.as_read_section()
}
pub fn find_object_after(&self, frame: usize) -> EditSectionResult<Option<ObjectHandle>> {
self.read_section().find_object_after(self.index, frame)
}
pub fn get_name(&self) -> EditSectionResult<Option<String>> {
self.read_section().get_layer_name(self.index)
}
pub fn objects(&self) -> EditSectionLayerObjectsIterator<'_, S> {
EditSectionLayerObjectsIterator::new(self.edit_section, self.index)
}
}
impl EditSectionLayerCaller<'_, EditSection> {
pub fn create_object_from_alias<P: AsRef<std::path::Path>>(
&self,
alias: &str,
frame: usize,
length: usize,
) -> EditSectionResult<ObjectHandle> {
self.edit_section
.create_object_from_alias(alias, self.index, frame, length)
}
pub fn create_object_from_media_file<P: AsRef<std::path::Path>>(
&self,
file_path: P,
frame: usize,
length: Option<usize>,
) -> EditSectionResult<ObjectHandle> {
self.edit_section
.create_object_from_media_file(file_path, self.index, frame, length)
}
pub fn create_object(
&self,
effect: &str,
frame: usize,
length: Option<usize>,
) -> EditSectionResult<ObjectHandle> {
self.edit_section
.create_object(effect, self.index, frame, length)
}
pub fn set_name(&self, name: Option<&str>) -> EditSectionResult<()> {
self.edit_section.set_layer_name(self.index, name)
}
}
#[derive(Debug, Clone)]
pub struct EditSectionLayersIterator<'a> {
edit_section: &'a EditSection,
current: usize,
total: usize,
}
impl<'a> EditSectionLayersIterator<'a> {
fn new(edit_section: &'a EditSection) -> Self {
Self {
edit_section,
current: 0,
total: edit_section.info.layer_max,
}
}
}
impl<'a> Iterator for EditSectionLayersIterator<'a> {
type Item = EditSectionLayerCaller<'a, EditSection>;
fn next(&mut self) -> Option<Self::Item> {
if self.current > self.total {
return None;
}
let layer = self.current;
self.current += 1;
Some(EditSectionLayerCaller::new(self.edit_section, layer))
}
}
#[derive(Debug, Clone)]
pub struct EditSectionLayerObjectsIterator<'a, S> {
edit_section: &'a S,
layer: usize,
next_frame: usize,
}
impl<'a, S> EditSectionLayerObjectsIterator<'a, S> {
fn new(edit_section: &'a S, layer: usize) -> Self {
Self {
edit_section,
layer,
next_frame: 0,
}
}
}
impl<S> Iterator for EditSectionLayerObjectsIterator<'_, S>
where
S: ReadSectionProvider,
{
type Item = (ObjectLayerFrame, ObjectHandle);
fn next(&mut self) -> Option<Self::Item> {
let read_section = self.edit_section.as_read_section();
let Ok(Some(handle)) = read_section.find_object_after(self.layer, self.next_frame) else {
return None;
};
let lf = match read_section.get_object_layer_frame(handle) {
Ok(lf) => lf,
Err(_) => return None,
};
self.next_frame = lf.end.saturating_add(1);
Some((lf, handle))
}
}
fn effect_key(effect_name: &str, effect_index: usize) -> String {
format!("{effect_name}:{effect_index}")
}