Skip to main content

freya_code_editor/
editor_line.rs

1use std::borrow::Cow;
2
3use freya_core::prelude::*;
4use freya_edit::{
5    EditableEvent,
6    EditorLine,
7    TextEditor,
8};
9use torin::{
10    gaps::Gaps,
11    prelude::Alignment,
12    size::Size,
13};
14
15use crate::{
16    editor_data::CodeEditorData,
17    editor_theme::EditorTheme,
18    syntax::TextNode,
19};
20
21#[derive(Clone, PartialEq)]
22pub struct EditorLineUI {
23    pub(crate) editor: Writable<CodeEditorData>,
24    pub(crate) font_size: f32,
25    pub(crate) line_height: f32,
26    pub(crate) line_index: usize,
27    pub(crate) read_only: bool,
28    pub(crate) gutter: bool,
29    pub(crate) show_whitespace: bool,
30    pub(crate) font_family: Cow<'static, str>,
31    pub(crate) theme: Readable<EditorTheme>,
32}
33
34impl Component for EditorLineUI {
35    fn render_key(&self) -> DiffKey {
36        DiffKey::from(&self.line_index)
37    }
38    fn render(&self) -> impl IntoElement {
39        let EditorLineUI {
40            mut editor,
41            font_size,
42            line_height,
43            line_index,
44            read_only,
45            gutter,
46            show_whitespace,
47            font_family,
48            theme,
49        } = self.clone();
50
51        let holder = use_state(ParagraphHolder::default);
52
53        let editor_data = editor.read();
54        let theme = theme.read();
55
56        let longest_width = editor_data.metrics.longest_width;
57        let line = editor_data.metrics.syntax_blocks.get_line(line_index);
58        let highlights = editor_data.get_visible_selection(EditorLine::Paragraph(line_index));
59        let gutter_width = font_size * 5.0;
60        let is_line_selected = editor_data.cursor_row() == line_index;
61
62        let on_mouse_down = {
63            let mut editor = editor.clone();
64            let font_family = font_family.clone();
65            move |e: Event<MouseEventData>| {
66                editor.write_if(|mut editor_editor| {
67                    editor_editor.process(
68                        font_size,
69                        &font_family,
70                        EditableEvent::Down {
71                            location: e.element_location,
72                            editor_line: EditorLine::Paragraph(line_index),
73                            holder: &holder.read(),
74                        },
75                    )
76                });
77            }
78        };
79
80        let on_mouse_move = {
81            let font_family = font_family.clone();
82            move |e: Event<MouseEventData>| {
83                editor.write_if(|mut editor_editor| {
84                    editor_editor.process(
85                        font_size,
86                        &font_family,
87                        EditableEvent::Move {
88                            location: e.element_location,
89                            editor_line: EditorLine::Paragraph(line_index),
90                            holder: &holder.read(),
91                        },
92                    )
93                });
94            }
95        };
96
97        let cursor_index = if read_only {
98            None
99        } else {
100            is_line_selected.then(|| editor_data.cursor_col())
101        };
102        let gutter_color = if is_line_selected {
103            theme.gutter_selected
104        } else {
105            theme.gutter_unselected
106        };
107        let visible_selection = match editor_data.get_selection() {
108            None => false,
109            Some((s, e)) if s != e => true,
110            _ => false,
111        };
112        let line_background = if is_line_selected && !visible_selection {
113            theme.line_selected_background
114        } else {
115            Color::TRANSPARENT
116        };
117
118        rect()
119            .horizontal()
120            .height(Size::px(line_height))
121            .background(line_background)
122            .font_size(font_size)
123            .maybe(gutter, |el| {
124                el.child(
125                    rect()
126                        .width(Size::px(gutter_width))
127                        .height(Size::fill())
128                        .padding(Gaps::new(0., 0., 0., 20.))
129                        .main_align(Alignment::Center)
130                        .child(
131                            label()
132                                .color(gutter_color)
133                                .text(format!("{} ", line_index + 1)),
134                        ),
135                )
136            })
137            .child(
138                paragraph()
139                    .holder(holder.read().clone())
140                    .on_mouse_down(on_mouse_down)
141                    .on_mouse_move(on_mouse_move)
142                    .cursor_color(theme.cursor)
143                    .cursor_style(CursorStyle::Block)
144                    .cursor_index(cursor_index)
145                    .cursor_mode(CursorMode::Expanded)
146                    .vertical_align(VerticalAlign::Center)
147                    .highlights(highlights.map(|h| vec![h]))
148                    .highlight_color(theme.highlight)
149                    .width(Size::px(longest_width))
150                    .min_width(Size::fill())
151                    .height(Size::fill())
152                    .font_family(font_family)
153                    .max_lines(1)
154                    .color(theme.text)
155                    .spans_iter(line.iter().map(|span| {
156                        let text: Cow<str> = match &span.1 {
157                            TextNode::Range(word_pos) => {
158                                editor_data.rope.slice(word_pos.clone()).into()
159                            }
160                            TextNode::LineOfChars { len, char } => {
161                                if show_whitespace {
162                                    Cow::Owned(char.to_string().repeat(*len))
163                                } else {
164                                    Cow::Owned(" ".repeat(*len))
165                                }
166                            }
167                        };
168                        Span::new(Cow::Owned(text.to_string())).color(span.0)
169                    })),
170            )
171    }
172}