freya_core/states/
cursor.rs1use 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}