freya_core/states/
cursor.rs

1use std::sync::{
2    Arc,
3    Mutex,
4};
5
6use freya_engine::prelude::*;
7use freya_native_core::{
8    attributes::AttributeName,
9    exports::shipyard::Component,
10    node::{
11        NodeType,
12        OwnedAttributeValue,
13    },
14    node_ref::NodeView,
15    prelude::{
16        AttributeMaskBuilder,
17        Dependancy,
18        NodeMaskBuilder,
19        OwnedAttributeView,
20        State,
21    },
22    tags::TagName,
23    NodeId,
24    SendAnyMap,
25};
26use freya_native_core_macro::partial_derive_state;
27
28use crate::{
29    custom_attributes::{
30        CursorReference,
31        CustomAttributeValues,
32    },
33    dom::{
34        CompositorDirtyNodes,
35        ParagraphElements,
36    },
37    parsing::{
38        Parse,
39        ParseAttribute,
40        ParseError,
41    },
42    values::{
43        CursorMode,
44        HighlightMode,
45    },
46};
47
48#[derive(Clone, Debug, PartialEq, Component)]
49pub struct CursorState {
50    pub position: Option<i32>,
51    pub color: Color,
52    pub mode: CursorMode,
53    pub cursor_id: Option<usize>,
54    pub highlights: Option<Vec<(usize, usize)>>,
55    pub highlight_color: Color,
56    pub highlight_mode: HighlightMode,
57    pub cursor_ref: Option<CursorReference>,
58}
59
60impl Default for CursorState {
61    fn default() -> Self {
62        Self {
63            position: None,
64            color: Color::BLACK,
65            mode: CursorMode::None,
66            cursor_id: None,
67            highlights: None,
68            highlight_color: Color::from_rgb(87, 108, 188),
69            highlight_mode: HighlightMode::default(),
70            cursor_ref: None,
71        }
72    }
73}
74
75impl ParseAttribute for CursorState {
76    fn parse_attribute(
77        &mut self,
78        attr: OwnedAttributeView<CustomAttributeValues>,
79    ) -> Result<(), ParseError> {
80        match attr.attribute {
81            AttributeName::CursorIndex => {
82                if let Some(value) = attr.value.as_text() {
83                    if value != "none" {
84                        self.position = Some(value.parse().map_err(|_| ParseError)?);
85                    }
86                }
87            }
88            AttributeName::CursorColor => {
89                if let Some(value) = attr.value.as_text() {
90                    self.color = Color::parse(value)?;
91                }
92            }
93            AttributeName::CursorMode => {
94                if let Some(value) = attr.value.as_text() {
95                    self.mode = CursorMode::parse(value)?;
96                }
97            }
98            AttributeName::CursorId => {
99                if let Some(value) = attr.value.as_text() {
100                    self.cursor_id = Some(value.parse().map_err(|_| ParseError)?);
101                }
102            }
103            AttributeName::Highlights => {
104                if let Some(CustomAttributeValues::TextHighlights(highlights)) =
105                    attr.value.as_custom()
106                {
107                    self.highlights = Some(highlights.clone());
108                }
109            }
110            AttributeName::HighlightColor => {
111                if let Some(value) = attr.value.as_text() {
112                    self.highlight_color = Color::parse(value)?;
113                }
114            }
115            AttributeName::HighlightMode => {
116                if let Some(value) = attr.value.as_text() {
117                    self.highlight_mode = HighlightMode::parse(value)?;
118                }
119            }
120            AttributeName::CursorReference => {
121                if let OwnedAttributeValue::Custom(CustomAttributeValues::CursorReference(
122                    reference,
123                )) = attr.value
124                {
125                    self.cursor_ref = Some(reference.clone());
126                }
127            }
128            _ => {}
129        }
130
131        Ok(())
132    }
133}
134
135#[partial_derive_state]
136impl State<CustomAttributeValues> for CursorState {
137    type ParentDependencies = (Self,);
138
139    type ChildDependencies = ();
140
141    type NodeDependencies = ();
142
143    const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
144        .with_attrs(AttributeMaskBuilder::Some(&[
145            AttributeName::CursorIndex,
146            AttributeName::CursorColor,
147            AttributeName::CursorMode,
148            AttributeName::CursorId,
149            AttributeName::Highlights,
150            AttributeName::HighlightColor,
151            AttributeName::HighlightMode,
152            AttributeName::CursorReference,
153        ]))
154        .with_tag();
155
156    fn allow_node(node_type: &NodeType<CustomAttributeValues>) -> bool {
157        node_type.tag() == Some(&TagName::Paragraph)
158    }
159
160    fn update<'a>(
161        &mut self,
162        node_view: NodeView<CustomAttributeValues>,
163        _node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
164        parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
165        _children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
166        context: &SendAnyMap,
167    ) -> bool {
168        let root_id = context.get::<NodeId>().unwrap();
169        let paragraphs = context.get::<Arc<Mutex<ParagraphElements>>>().unwrap();
170        let compositor_dirty_nodes = context.get::<Arc<Mutex<CompositorDirtyNodes>>>().unwrap();
171        let mut cursor = parent.map(|(p,)| p.clone()).unwrap_or_default();
172
173        if let Some(attributes) = node_view.attributes() {
174            for attr in attributes {
175                cursor.parse_safe(attr);
176            }
177        }
178        let changed = &cursor != self;
179
180        let is_orphan = node_view.height() == 0 && node_view.node_id() != *root_id;
181
182        if changed && CursorMode::Editable == cursor.mode && !is_orphan {
183            if let Some((tag, cursor_ref)) = node_view.tag().zip(cursor.cursor_ref.as_ref()) {
184                if *tag == TagName::Paragraph {
185                    paragraphs
186                        .lock()
187                        .unwrap()
188                        .insert_paragraph(node_view.node_id(), cursor_ref.text_id)
189                }
190            }
191            compositor_dirty_nodes
192                .lock()
193                .unwrap()
194                .invalidate(node_view.node_id());
195        }
196
197        *self = cursor;
198        changed
199    }
200}