freya_code_editor/
editor_line.rs1use 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}