rive_rs/
component.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::any::TypeId;
6use std::cell::Cell;
7use std::hash::{Hash, Hasher};
8use std::iter;
9
10use crate::artboard::Artboard;
11use crate::bones::Skin;
12use crate::component_dirt::ComponentDirt;
13use crate::container_component::ContainerComponent;
14use crate::core::{Core, CoreContext, Object, ObjectRef, OnAdded, Property};
15use crate::dyn_vec::DynVec;
16use crate::importers::{ArtboardImporter, ImportStack};
17use crate::option_cell::OptionCell;
18use crate::shapes::paint::LinearGradient;
19use crate::shapes::{
20    ClippingShape, Ellipse, Path, PathComposer, PointsPath, Polygon, Rectangle, Shape, Triangle,
21};
22use crate::status_code::StatusCode;
23use crate::TransformComponent;
24
25#[derive(Debug)]
26pub struct Component {
27    name: Property<String>,
28    parent_id: Property<u64>,
29    parent: OptionCell<Object<ContainerComponent>>,
30    depdenents: DynVec<Object<Self>>,
31    artboard: OptionCell<Object<Artboard>>,
32    pub(crate) graph_order: Cell<usize>,
33    pub(crate) dirt: Cell<ComponentDirt>,
34}
35
36impl<'a> ObjectRef<'a, Component> {
37    pub fn name(&self) -> String {
38        self.name.get()
39    }
40
41    pub fn set_name(&self, name: String) {
42        self.name.set(name);
43    }
44
45    pub fn parent_id(&'a self) -> u64 {
46        self.parent_id.get()
47    }
48
49    pub fn set_parent_id(&self, parent_id: u64) {
50        self.parent_id.set(parent_id);
51    }
52}
53
54impl Component {
55    pub fn artboard(&self) -> Option<Object<Artboard>> {
56        self.artboard.get()
57    }
58
59    pub fn value_has_dirt(value: ComponentDirt, flags: ComponentDirt) -> bool {
60        !(value & flags).is_empty()
61    }
62}
63
64impl ObjectRef<'_, Component> {
65    pub fn parent(&self) -> Option<Object<ContainerComponent>> {
66        self.parent.get()
67    }
68
69    pub fn parents(&self) -> impl Iterator<Item = Object<ContainerComponent>> {
70        iter::successors(self.parent(), |component| component.cast::<Component>().as_ref().parent())
71    }
72
73    pub fn depdenents(&self) -> impl Iterator<Item = Object<Component>> + '_ {
74        self.depdenents.iter()
75    }
76
77    pub fn push_dependent(&self, dependent: Object<Component>) {
78        self.depdenents.push(dependent);
79    }
80
81    pub fn has_dirt(&self, flags: ComponentDirt) -> bool {
82        self.dirt.get() & flags == flags
83    }
84
85    pub fn add_dirt(&self, value: ComponentDirt, recurse: bool) -> bool {
86        if self.has_dirt(value) {
87            return false;
88        }
89
90        self.dirt.set(self.dirt.get() | value);
91        self.on_dirty(self.dirt.get());
92
93        if let Some(artboard) = self.artboard.get() {
94            artboard.as_ref().on_component_dirty(self);
95        }
96
97        if !recurse {
98            return true;
99        }
100
101        for dependent in self.depdenents() {
102            dependent.as_ref().add_dirt(value, recurse);
103        }
104
105        true
106    }
107
108    pub fn build_dependencies(&self) {
109        match_cast!(self, {
110            ClippingShape(clipping_shape) => clipping_shape.build_dependencies(),
111            Shape(shape) => shape.build_dependencies(),
112            PointsPath(points_path) => points_path.build_dependencies(),
113            Path(path) => path.build_dependencies(),
114            PathComposer(path_composer) => path_composer.build_dependencies(),
115            Skin(skin) => skin.build_dependencies(),
116            LinearGradient(linear_gradient) => linear_gradient.build_dependencies(),
117            TransformComponent(transform_component) => transform_component.build_dependencies(),
118        })
119    }
120
121    pub fn on_dirty(&self, dirt: ComponentDirt) {
122        match_cast!(self, {
123            Path(path) => path.on_dirty(dirt),
124            Skin(skin) => skin.on_dirty(dirt),
125            Artboard(artboard) => artboard.on_dirty(dirt),
126        })
127    }
128
129    pub fn update(&self, value: ComponentDirt) {
130        match_cast!(self, {
131            Ellipse(ellipse) => ellipse.update(value),
132            Rectangle(rectangle) => rectangle.update(value),
133            Triangle(triangle) => triangle.update(value),
134            Polygon(polygon) => polygon.update(value),
135            ClippingShape(clipping_shape) => clipping_shape.update(value),
136            Shape(shape) => shape.update(value),
137            PointsPath(points_path) => points_path.update(value),
138            Path(path) => path.update(value),
139            PathComposer(path_composer) => path_composer.update(value),
140            Skin(skin) => skin.update(value),
141            LinearGradient(linear_gradient) => linear_gradient.update(value),
142            TransformComponent(transform_component) => transform_component.update(value),
143            Artboard(artboard) => artboard.update(value),
144        })
145    }
146}
147
148impl Hash for Component {
149    fn hash<H: Hasher>(&self, state: &mut H) {
150        self.name.get().hash(state);
151        self.parent_id.get().hash(state);
152    }
153}
154
155impl Core for Component {
156    properties![(4, name, set_name), (5, parent_id, set_parent_id)];
157}
158
159impl OnAdded for ObjectRef<'_, Component> {
160    on_added!([on_added_clean]);
161
162    fn on_added_dirty(&self, context: &dyn CoreContext) -> StatusCode {
163        let artboard = context.artboard();
164        self.artboard.set(Some(artboard.clone()));
165
166        if let Some(self_artboard) = self.as_object().try_cast() {
167            if artboard == self_artboard {
168                return StatusCode::Ok;
169            }
170        }
171
172        if let Some(parent) =
173            context.resolve(self.parent_id() as usize).and_then(|core| core.try_cast())
174        {
175            self.parent.set(Some(parent));
176            StatusCode::Ok
177        } else {
178            StatusCode::MissingObject
179        }
180    }
181
182    fn import(&self, object: Object, import_stack: &ImportStack) -> StatusCode {
183        if let Some(importer) = import_stack.latest::<ArtboardImporter>(TypeId::of::<Artboard>()) {
184            importer.push_object(object);
185            StatusCode::Ok
186        } else {
187            StatusCode::MissingObject
188        }
189    }
190}
191
192impl Default for Component {
193    fn default() -> Self {
194        Self {
195            name: Property::new(String::new()),
196            parent_id: Property::new(0),
197            parent: OptionCell::new(),
198            depdenents: DynVec::new(),
199            artboard: OptionCell::new(),
200            graph_order: Cell::new(0),
201            dirt: Cell::new(ComponentDirt::all()),
202        }
203    }
204}