1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use crate::{
    component::Component,
    component_dirt::ComponentDirt,
    core::{Core, CoreContext, Object, ObjectRef, OnAdded, Property, TryFromU64},
    drawable::Drawable,
    dyn_vec::DynVec,
    option_cell::OptionCell,
    shapes::ClippingShape,
    status_code::StatusCode,
};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DrawTargetPlacement {
    Before,
    After,
}

impl Default for DrawTargetPlacement {
    fn default() -> Self {
        Self::Before
    }
}

impl TryFromU64 for DrawTargetPlacement {
    fn try_from(val: u64) -> Option<Self> {
        match val {
            0 => Some(Self::Before),
            1 => Some(Self::After),
            _ => None,
        }
    }
}

#[derive(Debug)]
pub struct DrawTarget {
    component: Component,
    drawable_id: Property<u64>,
    placement: Property<DrawTargetPlacement>,
    drawable: OptionCell<Object<Drawable>>,
    pub(crate) first: OptionCell<Object<Drawable>>,
    pub(crate) last: OptionCell<Object<Drawable>>,
    clipping_shapes: DynVec<Object<ClippingShape>>,
}

impl ObjectRef<'_, DrawTarget> {
    pub fn drawable_id(&self) -> u64 {
        self.drawable_id.get()
    }

    pub fn set_drawable_id(&self, drawable_id: u64) {
        self.drawable_id.set(drawable_id);
    }

    pub fn placement(&self) -> DrawTargetPlacement {
        self.placement.get()
    }

    pub fn set_placement(&self, placement: DrawTargetPlacement) {
        if self.placement() == placement {
            return;
        }

        self.placement.set(placement);

        if let Some(artboard) = self.component.artboard() {
            artboard.cast::<Component>().as_ref().add_dirt(ComponentDirt::DRAW_ORDER, false);
        }
    }
}

impl ObjectRef<'_, DrawTarget> {
    pub fn drawable(&self) -> Option<Object<Drawable>> {
        self.drawable.get()
    }

    pub fn push_clipping_shape(&self, clipping_shape: Object<ClippingShape>) {
        self.clipping_shapes.push(clipping_shape);
    }

    pub fn clipping_shapes(&self) -> impl Iterator<Item = Object<ClippingShape>> + '_ {
        self.clipping_shapes.iter()
    }
}

impl Core for DrawTarget {
    parent_types![(component, Component)];

    properties![(119, drawable_id, set_drawable_id), (120, placement, set_placement), component];
}

impl OnAdded for ObjectRef<'_, DrawTarget> {
    on_added!([on_added_clean, 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(drawable) =
            context.resolve(self.drawable_id() as usize).and_then(|core| core.try_cast())
        {
            self.drawable.set(Some(drawable));
            StatusCode::Ok
        } else {
            StatusCode::MissingObject
        }
    }
}

impl Default for DrawTarget {
    fn default() -> Self {
        Self {
            component: Component::default(),
            drawable_id: Property::new(0),
            placement: Property::new(DrawTargetPlacement::Before),
            drawable: OptionCell::new(),
            first: OptionCell::new(),
            last: OptionCell::new(),
            clipping_shapes: DynVec::new(),
        }
    }
}