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 id::ViewId,
11 style::{CursorColor, Style},
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 child: ViewId,
41 cx: Scope,
43 editor: Editor,
44}
45
46pub fn text_editor(text: impl Into<Rope>) -> TextEditor {
80 let id = ViewId::new();
81 let cx = Scope::current();
82
83 let doc = Rc::new(TextDocument::new(cx, text));
84 let style = Rc::new(SimpleStyling::new());
85 let editor = Editor::new(cx, doc, style, false);
86
87 let editor_sig = cx.create_rw_signal(editor.clone());
88 let child = cx
89 .enter(|| editor_container_view(editor_sig, |_| true, default_key_handler(editor_sig)))
90 .into_view();
91
92 let child_id = child.id();
93 id.set_children([child]);
94
95 TextEditor {
96 id,
97 child: child_id,
98 cx,
99 editor,
100 }
101}
102
103pub fn text_editor_keys(
107 text: impl Into<Rope>,
108 handle_key_event: impl Fn(RwSignal<Editor>, &KeypressKey) -> CommandExecuted + 'static,
109) -> TextEditor {
110 let id = ViewId::new();
111 let cx = Scope::current();
112
113 let doc = Rc::new(TextDocument::new(cx, text));
114 let style = Rc::new(SimpleStyling::new());
115 let editor = Editor::new(cx, doc, style, false);
116
117 let editor_sig = cx.create_rw_signal(editor.clone());
118 let child = cx
119 .enter(|| {
120 editor_container_view(
121 editor_sig,
122 |_| true,
123 move |kp| handle_key_event(editor_sig, &kp),
124 )
125 })
126 .into_view();
127
128 let child_id = child.id();
129 id.set_children([child]);
130
131 TextEditor {
132 id,
133 cx,
134 editor,
135 child: child_id,
136 }
137}
138
139impl View for TextEditor {
140 fn id(&self) -> ViewId {
141 self.id
142 }
143
144 fn view_style(&self) -> Option<Style> {
145 Some(Style::new().min_width(25).min_height(10))
146 }
147
148 fn debug_name(&self) -> std::borrow::Cow<'static, str> {
149 "Text Editor".into()
150 }
151
152 fn paint(&mut self, cx: &mut crate::context::PaintCx) {
153 cx.save();
154 let size = self
155 .id
156 .get_layout()
157 .map(|layout| {
158 peniko::kurbo::Size::new(layout.size.width as f64, layout.size.height as f64)
159 })
160 .unwrap_or_default();
161 let border_radii =
162 crate::view::border_to_radii(&self.id.state().borrow().combined_style, size);
163
164 if crate::view::radii_max(border_radii) > 0.0 {
165 let rect = size.to_rect().to_rounded_rect(border_radii);
166 cx.clip(&rect);
167 } else {
168 cx.clip(&size.to_rect());
169 }
170 cx.paint_view(self.child);
171 cx.restore();
172 }
173}
174
175pub struct EditorCustomStyle(pub(crate) Style);
177
178impl EditorCustomStyle {
179 pub fn hide_gutter(mut self, hide: bool) -> Self {
181 self.0 = self
182 .0
183 .class(GutterClass, |s| s.apply_if(hide, |s| s.hide()));
184 self
185 }
186
187 pub fn gutter_accent_color(mut self, color: Color) -> Self {
192 self.0 = self.0.class(GutterClass, |s| s.color(color));
193 self
194 }
195
196 pub fn gutter_dim_color(mut self, color: Color) -> Self {
201 self.0 = self.0.class(GutterClass, |s| s.set(DimColor, color));
202 self
203 }
204
205 pub fn gutter_left_padding(mut self, padding: f64) -> Self {
207 self.0 = self
208 .0
209 .class(GutterClass, |s| s.set(LeftOfCenterPadding, padding));
210 self
211 }
212
213 pub fn gutter_right_padding(mut self, padding: f64) -> Self {
215 self.0 = self
216 .0
217 .class(GutterClass, |s| s.set(RightOfCenterPadding, padding));
218 self
219 }
220
221 pub fn gutter_current_color(mut self, color: Color) -> Self {
223 self.0 = self
224 .0
225 .class(GutterClass, |s| s.set(CurrentLineColor, color));
226 self
227 }
228
229 pub fn selection_color(mut self, color: Color) -> Self {
231 self.0 = self
232 .0
233 .class(EditorViewClass, |s| s.set(SelectionColor, color));
234 self
235 }
236
237 pub fn indent_style(mut self, indent_style: IndentStyle) -> Self {
239 self.0 = self
240 .0
241 .class(EditorViewClass, |s| s.set(IndentStyleProp, indent_style));
242 self
243 }
244
245 pub fn indent_guide_color(mut self, color: Color) -> Self {
247 self.0 = self
248 .0
249 .class(EditorViewClass, |s| s.set(IndentGuideColor, color));
250 self
251 }
252
253 pub fn wrap_method(mut self, wrap: WrapMethod) -> Self {
255 self.0 = self.0.class(EditorViewClass, |s| s.set(WrapProp, wrap));
256 self
257 }
258
259 pub fn cursor_color(mut self, cursor: Color) -> Self {
261 self.0 = self
262 .0
263 .class(EditorViewClass, |s| s.set(CursorColor, cursor));
264 self
265 }
266
267 pub fn scroll_beyond_last_line(mut self, scroll_beyond: bool) -> Self {
269 self.0 = self.0.class(EditorViewClass, |s| {
270 s.set(ScrollBeyondLastLine, scroll_beyond)
271 });
272 self
273 }
274
275 pub fn current_line_color(mut self, color: Color) -> Self {
277 self.0 = self
278 .0
279 .class(EditorViewClass, |s| s.set(CurrentLineColor, color));
280 self
281 }
282
283 pub fn visible_whitespace(mut self, color: Color) -> Self {
285 self.0 = self
286 .0
287 .class(EditorViewClass, |s| s.set(VisibleWhitespaceColor, color));
288 self
289 }
290
291 pub fn render_white_space(mut self, render_white_space: RenderWhitespace) -> Self {
293 self.0 = self.0.class(EditorViewClass, |s| {
294 s.set(RenderWhitespaceProp, render_white_space)
295 });
296 self
297 }
298
299 pub fn cursor_surrounding_lines(mut self, lines: usize) -> Self {
302 self.0 = self
303 .0
304 .class(EditorViewClass, |s| s.set(CursorSurroundingLines, lines));
305 self
306 }
307
308 pub fn indent_guide(mut self, show: bool) -> Self {
310 self.0 = self
311 .0
312 .class(EditorViewClass, |s| s.set(ShowIndentGuide, show));
313 self
314 }
315
316 pub fn modal(mut self, modal: bool) -> Self {
318 self.0 = self.0.class(EditorViewClass, |s| s.set(Modal, modal));
319 self
320 }
321
322 pub fn modal_relative_line(mut self, modal_relative_line: bool) -> Self {
324 self.0 = self.0.class(EditorViewClass, |s| {
325 s.set(ModalRelativeLine, modal_relative_line)
326 });
327 self
328 }
329
330 pub fn smart_tab(mut self, smart_tab: bool) -> Self {
332 self.0 = self
333 .0
334 .class(EditorViewClass, |s| s.set(SmartTab, smart_tab));
335 self
336 }
337
338 pub fn phantom_color(mut self, color: Color) -> Self {
340 self.0 = self
341 .0
342 .class(EditorViewClass, |s| s.set(PhantomColor, color));
343 self
344 }
345
346 pub fn placeholder_color(mut self, color: Color) -> Self {
348 self.0 = self
349 .0
350 .class(EditorViewClass, |s| s.set(PlaceholderColor, color));
351 self
352 }
353
354 pub fn preedit_underline_color(mut self, color: Color) -> Self {
356 self.0 = self
357 .0
358 .class(EditorViewClass, |s| s.set(PreeditUnderlineColor, color));
359 self
360 }
361}
362
363impl TextEditor {
364 pub fn editor_style(
366 self,
367 style: impl Fn(EditorCustomStyle) -> EditorCustomStyle + 'static,
368 ) -> Self {
369 let id = self.id();
370 let view_state = id.state();
371 let offset = view_state.borrow_mut().style.next_offset();
372 let style = UpdaterEffect::new(
373 move || style(EditorCustomStyle(Style::new())),
374 move |style| id.update_style(offset, style.0),
375 );
376 view_state.borrow_mut().style.push(style.0);
377 self
378 }
379
380 pub fn editor(&self) -> &Editor {
382 &self.editor
383 }
384
385 pub fn with_editor(self, f: impl FnOnce(&Editor)) -> Self {
387 f(&self.editor);
388 self
389 }
390
391 pub fn with_editor_mut(mut self, f: impl FnOnce(&mut Editor)) -> Self {
393 f(&mut self.editor);
394 self
395 }
396
397 pub fn editor_id(&self) -> EditorId {
399 self.editor.id()
400 }
401
402 pub fn with_doc(self, f: impl FnOnce(&dyn Document)) -> Self {
405 self.editor.doc.with_untracked(|doc| {
406 f(doc.as_ref());
407 });
408 self
409 }
410
411 pub fn doc(&self) -> Rc<dyn Document> {
413 self.editor.doc()
414 }
415
416 fn text_doc(&self) -> Option<Rc<TextDocument>> {
419 (self.doc() as Rc<dyn ::std::any::Any>).downcast().ok()
420 }
421
422 pub fn rope_text(&self) -> RopeTextVal {
424 self.editor.rope_text()
425 }
426
427 pub fn use_doc(self, doc: Rc<dyn Document>) -> Self {
429 self.editor.update_doc(doc, None);
430 self
431 }
432
433 pub fn share_doc(self, other: &TextEditor) -> Self {
448 self.use_doc(other.editor.doc())
449 }
450
451 pub fn shared_editor(&self) -> TextEditor {
460 let id = ViewId::new();
461
462 let doc = self.editor.doc();
463 let style = self.editor.style();
464 let editor = Editor::new(self.cx, doc, style, false);
465
466 let editor_sig = self.cx.create_rw_signal(editor.clone());
467 let child = self
468 .cx
469 .enter(|| editor_container_view(editor_sig, |_| true, default_key_handler(editor_sig)))
470 .into_view();
471
472 let child_id = child.id();
473 id.set_children([child]);
474
475 TextEditor {
476 id,
477 cx: self.cx,
478 editor,
479 child: child_id,
480 }
481 }
482
483 pub fn styling(self, styling: impl Styling + 'static) -> Self {
492 self.styling_rc(Rc::new(styling))
493 }
494
495 pub fn styling_rc(self, styling: Rc<dyn Styling>) -> Self {
497 self.editor.update_styling(styling);
498 self
499 }
500
501 pub fn read_only(self) -> Self {
505 self.editor.read_only.set(true);
506 self
507 }
508
509 pub fn placeholder(self, text: impl Into<String>) -> Self {
517 if let Some(doc) = self.text_doc() {
518 doc.add_placeholder(self.editor_id(), text.into());
519 }
520
521 self
522 }
523
524 pub fn pre_command(self, f: impl Fn(PreCommand) -> CommandExecuted + 'static) -> Self {
555 if let Some(doc) = self.text_doc() {
556 doc.add_pre_command(self.editor.id(), f);
557 }
558 self
559 }
560
561 pub fn update(self, f: impl Fn(OnUpdate) + 'static) -> Self {
568 if let Some(doc) = self.text_doc() {
569 doc.add_on_update(f);
570 }
571 self
572 }
573}