1use std::rc::Rc;
2
3use floem_editor_core::{buffer::rope_text::RopeTextVal, indent::IndentStyle};
4use floem_reactive::{RwSignal, Scope, SignalUpdate, SignalWith, UpdaterEffect};
5use peniko::Color;
6
7use lapce_xi_rope::Rope;
8
9use crate::{
10 style::{CursorColor, Style},
11 view::ViewId,
12 view::{IntoView, View},
13 views::editor::{
14 Editor,
15 command::CommandExecuted,
16 id::EditorId,
17 keypress::{KeypressKey, default_key_handler},
18 text::{Document, SimpleStyling, Styling},
19 text_document::{OnUpdate, PreCommand, TextDocument},
20 view::editor_container_view,
21 },
22};
23
24use super::editor::{
25 CurrentLineColor, CursorSurroundingLines, IndentGuideColor, IndentStyleProp, Modal,
26 ModalRelativeLine, PhantomColor, PlaceholderColor, PreeditUnderlineColor, RenderWhitespaceProp,
27 ScrollBeyondLastLine, SelectionColor, ShowIndentGuide, SmartTab, VisibleWhitespaceColor,
28 WrapProp,
29 gutter::{DimColor, GutterClass, LeftOfCenterPadding, RightOfCenterPadding},
30 text::{RenderWhitespace, WrapMethod},
31 view::EditorViewClass,
32};
33
34pub struct TextEditor {
39 id: ViewId,
40 cx: Scope,
42 editor: Editor,
43}
44
45pub fn text_editor(text: impl Into<Rope>) -> TextEditor {
79 let id = ViewId::new();
80 let cx = Scope::current();
81
82 let doc = Rc::new(TextDocument::new(cx, text));
83 let style = Rc::new(SimpleStyling::new());
84 let editor = Editor::new(cx, doc, style, false);
85
86 let editor_sig = cx.create_rw_signal(editor.clone());
87 let child = cx
88 .enter(|| editor_container_view(editor_sig, |_| true, default_key_handler(editor_sig)))
89 .into_view();
90
91 id.set_children([child]);
92
93 TextEditor { id, cx, editor }
94}
95
96pub fn text_editor_keys(
100 text: impl Into<Rope>,
101 handle_key_event: impl Fn(RwSignal<Editor>, &KeypressKey) -> CommandExecuted + 'static,
102) -> TextEditor {
103 let id = ViewId::new();
104 let cx = Scope::current();
105
106 let doc = Rc::new(TextDocument::new(cx, text));
107 let style = Rc::new(SimpleStyling::new());
108 let editor = Editor::new(cx, doc, style, false);
109
110 let editor_sig = cx.create_rw_signal(editor.clone());
111 let child = cx
112 .enter(|| {
113 editor_container_view(
114 editor_sig,
115 |_| true,
116 move |kp| handle_key_event(editor_sig, &kp),
117 )
118 })
119 .into_view();
120
121 id.set_children([child]);
122
123 TextEditor { id, cx, editor }
124}
125
126impl View for TextEditor {
127 fn id(&self) -> ViewId {
128 self.id
129 }
130
131 fn view_style(&self) -> Option<Style> {
132 Some(Style::new().min_width(25).min_height(10))
133 }
134
135 fn debug_name(&self) -> std::borrow::Cow<'static, str> {
136 "Text Editor".into()
137 }
138
139 fn paint(&mut self, _cx: &mut crate::context::PaintCx) {
140 }
144}
145
146pub struct EditorCustomStyle(pub(crate) Style);
148
149impl EditorCustomStyle {
150 pub fn hide_gutter(mut self, hide: bool) -> Self {
152 self.0 = self
153 .0
154 .class(GutterClass, |s| s.apply_if(hide, |s| s.hide()));
155 self
156 }
157
158 pub fn gutter_accent_color(mut self, color: Color) -> Self {
163 self.0 = self.0.class(GutterClass, |s| s.color(color));
164 self
165 }
166
167 pub fn gutter_dim_color(mut self, color: Color) -> Self {
172 self.0 = self.0.class(GutterClass, |s| s.set(DimColor, color));
173 self
174 }
175
176 pub fn gutter_left_padding(mut self, padding: f64) -> Self {
178 self.0 = self
179 .0
180 .class(GutterClass, |s| s.set(LeftOfCenterPadding, padding));
181 self
182 }
183
184 pub fn gutter_right_padding(mut self, padding: f64) -> Self {
186 self.0 = self
187 .0
188 .class(GutterClass, |s| s.set(RightOfCenterPadding, padding));
189 self
190 }
191
192 pub fn gutter_current_color(mut self, color: Color) -> Self {
194 self.0 = self
195 .0
196 .class(GutterClass, |s| s.set(CurrentLineColor, color));
197 self
198 }
199
200 pub fn selection_color(mut self, color: Color) -> Self {
202 self.0 = self
203 .0
204 .class(EditorViewClass, |s| s.set(SelectionColor, color));
205 self
206 }
207
208 pub fn indent_style(mut self, indent_style: IndentStyle) -> Self {
210 self.0 = self
211 .0
212 .class(EditorViewClass, |s| s.set(IndentStyleProp, indent_style));
213 self
214 }
215
216 pub fn indent_guide_color(mut self, color: Color) -> Self {
218 self.0 = self
219 .0
220 .class(EditorViewClass, |s| s.set(IndentGuideColor, color));
221 self
222 }
223
224 pub fn wrap_method(mut self, wrap: WrapMethod) -> Self {
226 self.0 = self.0.class(EditorViewClass, |s| s.set(WrapProp, wrap));
227 self
228 }
229
230 pub fn cursor_color(mut self, cursor: Color) -> Self {
232 self.0 = self
233 .0
234 .class(EditorViewClass, |s| s.set(CursorColor, cursor));
235 self
236 }
237
238 pub fn scroll_beyond_last_line(mut self, scroll_beyond: bool) -> Self {
240 self.0 = self.0.class(EditorViewClass, |s| {
241 s.set(ScrollBeyondLastLine, scroll_beyond)
242 });
243 self
244 }
245
246 pub fn current_line_color(mut self, color: Color) -> Self {
248 self.0 = self
249 .0
250 .class(EditorViewClass, |s| s.set(CurrentLineColor, color));
251 self
252 }
253
254 pub fn visible_whitespace(mut self, color: Color) -> Self {
256 self.0 = self
257 .0
258 .class(EditorViewClass, |s| s.set(VisibleWhitespaceColor, color));
259 self
260 }
261
262 pub fn render_white_space(mut self, render_white_space: RenderWhitespace) -> Self {
264 self.0 = self.0.class(EditorViewClass, |s| {
265 s.set(RenderWhitespaceProp, render_white_space)
266 });
267 self
268 }
269
270 pub fn cursor_surrounding_lines(mut self, lines: usize) -> Self {
273 self.0 = self
274 .0
275 .class(EditorViewClass, |s| s.set(CursorSurroundingLines, lines));
276 self
277 }
278
279 pub fn indent_guide(mut self, show: bool) -> Self {
281 self.0 = self
282 .0
283 .class(EditorViewClass, |s| s.set(ShowIndentGuide, show));
284 self
285 }
286
287 pub fn modal(mut self, modal: bool) -> Self {
289 self.0 = self.0.class(EditorViewClass, |s| s.set(Modal, modal));
290 self
291 }
292
293 pub fn modal_relative_line(mut self, modal_relative_line: bool) -> Self {
295 self.0 = self.0.class(EditorViewClass, |s| {
296 s.set(ModalRelativeLine, modal_relative_line)
297 });
298 self
299 }
300
301 pub fn smart_tab(mut self, smart_tab: bool) -> Self {
303 self.0 = self
304 .0
305 .class(EditorViewClass, |s| s.set(SmartTab, smart_tab));
306 self
307 }
308
309 pub fn phantom_color(mut self, color: Color) -> Self {
311 self.0 = self
312 .0
313 .class(EditorViewClass, |s| s.set(PhantomColor, color));
314 self
315 }
316
317 pub fn placeholder_color(mut self, color: Color) -> Self {
319 self.0 = self
320 .0
321 .class(EditorViewClass, |s| s.set(PlaceholderColor, color));
322 self
323 }
324
325 pub fn preedit_underline_color(mut self, color: Color) -> Self {
327 self.0 = self
328 .0
329 .class(EditorViewClass, |s| s.set(PreeditUnderlineColor, color));
330 self
331 }
332}
333
334impl TextEditor {
335 pub fn editor_style(
337 self,
338 style: impl Fn(EditorCustomStyle) -> EditorCustomStyle + 'static,
339 ) -> Self {
340 let id = self.id();
341 let view_state = id.state();
342 let offset = view_state.borrow_mut().style.next_offset();
343 let style = UpdaterEffect::new(
344 move || style(EditorCustomStyle(Style::new())),
345 move |style| id.update_style(offset, style.0),
346 );
347 view_state.borrow_mut().style.push(style.0);
348 self
349 }
350
351 pub fn editor(&self) -> &Editor {
353 &self.editor
354 }
355
356 pub fn with_editor(self, f: impl FnOnce(&Editor)) -> Self {
358 f(&self.editor);
359 self
360 }
361
362 pub fn with_editor_mut(mut self, f: impl FnOnce(&mut Editor)) -> Self {
364 f(&mut self.editor);
365 self
366 }
367
368 pub fn editor_id(&self) -> EditorId {
370 self.editor.id()
371 }
372
373 pub fn with_doc(self, f: impl FnOnce(&dyn Document)) -> Self {
376 self.editor.doc.with_untracked(|doc| {
377 f(doc.as_ref());
378 });
379 self
380 }
381
382 pub fn doc(&self) -> Rc<dyn Document> {
384 self.editor.doc()
385 }
386
387 pub fn try_doc(&self) -> Option<Rc<dyn Document>> {
390 self.editor.try_doc()
391 }
392
393 pub fn try_doc_untracked(&self) -> Option<Rc<dyn Document>> {
396 self.editor.try_doc_untracked()
397 }
398
399 fn text_doc(&self) -> Option<Rc<TextDocument>> {
402 (self.doc() as Rc<dyn ::std::any::Any>).downcast().ok()
403 }
404
405 pub fn rope_text(&self) -> RopeTextVal {
407 self.editor.rope_text()
408 }
409
410 pub fn use_doc(self, doc: Rc<dyn Document>) -> Self {
412 self.editor.update_doc(doc, None);
413 self
414 }
415
416 pub fn share_doc(self, other: &TextEditor) -> Self {
431 self.use_doc(other.editor.doc())
432 }
433
434 pub fn shared_editor(&self) -> TextEditor {
443 let id = ViewId::new();
444
445 let doc = self.editor.doc();
446 let style = self.editor.style();
447 let editor = Editor::new(self.cx, doc, style, false);
448
449 let editor_sig = self.cx.create_rw_signal(editor.clone());
450 let child = self
451 .cx
452 .enter(|| editor_container_view(editor_sig, |_| true, default_key_handler(editor_sig)))
453 .into_view();
454
455 id.set_children([child]);
456
457 TextEditor {
458 id,
459 cx: self.cx,
460 editor,
461 }
462 }
463
464 pub fn styling(self, styling: impl Styling + 'static) -> Self {
473 self.styling_rc(Rc::new(styling))
474 }
475
476 pub fn styling_rc(self, styling: Rc<dyn Styling>) -> Self {
478 self.editor.update_styling(styling);
479 self
480 }
481
482 pub fn read_only(self) -> Self {
486 self.editor.read_only.set(true);
487 self
488 }
489
490 pub fn placeholder(self, text: impl Into<String>) -> Self {
498 if let Some(doc) = self.text_doc() {
499 doc.add_placeholder(self.editor_id(), text.into());
500 }
501
502 self
503 }
504
505 pub fn pre_command(self, f: impl Fn(PreCommand) -> CommandExecuted + 'static) -> Self {
536 if let Some(doc) = self.text_doc() {
537 doc.add_pre_command(self.editor.id(), f);
538 }
539 self
540 }
541
542 pub fn update(self, f: impl Fn(OnUpdate) + 'static) -> Self {
549 if let Some(doc) = self.text_doc() {
550 doc.add_on_update(f);
551 }
552 self
553 }
554}