rive_rs/shapes/
clipping_shape.rsuse std::{fmt, iter};
use crate::component::Component;
use crate::component_dirt::ComponentDirt;
use crate::core::{Core, CoreContext, Object, ObjectRef, OnAdded, Property};
use crate::drawable::Drawable;
use crate::dyn_vec::DynVec;
use crate::node::Node;
use crate::option_cell::OptionCell;
use crate::shapes::{FillRule, PathSpace, Shape};
use crate::status_code::StatusCode;
use crate::ContainerComponent;
use super::command_path::CommandPathBuilder;
use super::{CommandPath, ShapePaintContainer};
pub struct ClippingShape {
component: Component,
source_id: Property<u64>,
fill_rule: Property<FillRule>,
is_visible: Property<bool>,
shapes: DynVec<Object<Shape>>,
source: OptionCell<Object<Node>>,
command_path: OptionCell<CommandPath>,
}
impl fmt::Debug for ClippingShape {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.command_path.with(|command_path| {
f.debug_struct("ClippingShape")
.field("component", &self.component)
.field("source_id", &self.source_id)
.field("fill_rule", &self.fill_rule)
.field("is_visible", &self.is_visible)
.field("shapes", &self.shapes)
.field("source", &self.source)
.field("command_path", &command_path)
.finish()
})
}
}
impl ObjectRef<'_, ClippingShape> {
pub fn source_id(&self) -> u64 {
self.source_id.get()
}
pub fn set_source_id(&self, source_id: u64) {
self.source_id.set(source_id);
}
pub fn fill_rule(&self) -> FillRule {
self.fill_rule.get()
}
pub fn set_fill_rule(&self, fill_rule: FillRule) {
self.fill_rule.set(fill_rule);
}
pub fn is_visible(&self) -> bool {
self.is_visible.get()
}
pub fn set_is_visible(&self, is_visible: bool) {
self.is_visible.set(is_visible);
}
}
impl ObjectRef<'_, ClippingShape> {
pub(crate) fn with_command_path(&self, f: impl FnMut(Option<&CommandPath>)) {
self.command_path.with(f);
}
pub fn build_dependencies(&self) {
for shape in self.shapes.iter() {
shape
.as_ref()
.path_composer()
.cast::<Component>()
.push_dependent(self.as_object().cast());
}
}
pub fn update(&self, value: ComponentDirt) {
if Component::value_has_dirt(value, ComponentDirt::PATH | ComponentDirt::WORLD_TRANSFORM) {
let mut builder = CommandPathBuilder::new();
for shape in self.shapes.iter() {
shape.as_ref().path_composer().with_world_path(|path| {
builder.path(
path.expect("world_path should already be set on PathComposer"),
None,
);
});
}
self.command_path.set(Some(builder.build()));
}
}
}
impl Core for ClippingShape {
parent_types![(component, Component)];
properties![
(92, source_id, set_source_id),
(93, fill_rule, set_fill_rule),
(94, is_visible, set_is_visible),
component,
];
}
impl OnAdded for ObjectRef<'_, ClippingShape> {
on_added!([import], Component);
fn on_added_dirty(&self, context: &dyn CoreContext) -> StatusCode {
let code = self.cast::<Component>().on_added_dirty(context);
if code != StatusCode::Ok {
return code;
}
if let Some(node) =
context.resolve(self.source_id() as usize).and_then(|object| object.try_cast::<Node>())
{
self.source.set(Some(node));
StatusCode::Ok
} else {
StatusCode::MissingObject
}
}
fn on_added_clean(&self, context: &dyn CoreContext) -> StatusCode {
let clipping_holder = self.cast::<Component>().parent();
let artboard = context.artboard();
for object in &*artboard.as_ref().objects() {
if let Some(drawable) = object.try_cast::<Drawable>() {
let drawable = drawable.as_ref();
let mut components = iter::once(drawable.as_object().cast())
.chain(drawable.cast::<Component>().parents());
if components.any(|component| Some(component) == clipping_holder) {
drawable.push_clipping_shape(self.as_object());
}
}
if let Some(shape) = object.try_cast::<Shape>() {
let component = object.cast::<ContainerComponent>();
if Some(component.clone()) != clipping_holder {
let mut parents = iter::once(component.clone())
.chain(component.as_ref().cast::<Component>().parents());
let source = self.source.get().map(|source| source.cast());
if parents.any(|component| Some(component) == source) {
shape
.as_ref()
.cast::<ShapePaintContainer>()
.add_default_path_space(PathSpace::WORLD | PathSpace::CLIPPING);
self.shapes.push(shape);
}
}
}
}
StatusCode::Ok
}
}
impl Default for ClippingShape {
fn default() -> Self {
Self {
component: Component::default(),
source_id: Property::new(0),
fill_rule: Property::new(FillRule::NonZero),
is_visible: Property::new(true),
shapes: DynVec::new(),
source: OptionCell::new(),
command_path: OptionCell::new(),
}
}
}