duat_core/context/handles.rs
1//! Widget handles for Duat
2//!
3//! These are used pretty much everywhere, and are essentially just an
4//! [`RwData<W>`] conjoined to an [`Ui::Area`].
5
6use std::{
7 any::TypeId,
8 cell::{Cell, RefCell, RefMut},
9 rc::Rc,
10};
11
12use lender::Lender;
13
14use super::FileParts;
15use crate::{
16 cfg::PrintCfg,
17 data::{Pass, RwData},
18 file::File,
19 mode::{Cursor, Cursors, Selection, Selections},
20 text::{Point, Searcher, Text, TwoPoints},
21 ui::{Node, RawArea, Ui, Widget},
22};
23
24/// A handle to a [`File`] widget
25///
26/// This handle acts much like an [`RwData<File>`], but it also
27/// includes an [`Area`] that can be acted upon alongside the
28/// [`File`].
29///
30/// This is the only way you are supposed to read information about
31/// the [`File`], in order to display it on [`Widget`]s, create
32/// [`Text`]s, and do all sorts of things. You can, of course, also
33/// modify a [`File`] from within this struct, but you should be
34/// careful to prevent infinite loops, where you modify a [`File`], it
35/// gets updated, and then you modify it again after noticing that it
36/// has changed.
37///
38/// The main difference between a [`FileHandle<U>`] and a
39/// [`Handle<File<U>, U>`] is that the [`Handle`] is capable of acting
40/// on selections, but is fixed to just one [`File`], while the
41/// [`FileHandle`] can automatically point to the current [`File`].
42///
43/// [`Area`]: crate::ui::RawArea
44/// [`Text`]: crate::text::Text
45#[derive(Clone)]
46pub struct FileHandle<U: Ui> {
47 fixed: Option<FileParts<U>>,
48 current: RwData<Option<FileParts<U>>>,
49}
50
51impl<U: Ui> FileHandle<U> {
52 /// Returns a new [`FileHandle`] from its parts
53 pub(crate) fn from_parts(
54 fixed: Option<FileParts<U>>,
55 current: RwData<Option<FileParts<U>>>,
56 ) -> Self {
57 Self { fixed, current }
58 }
59
60 /// Reads from the [`File`] and the [`Area`] using a [`Pass`]
61 ///
62 /// The consistent use of a [`Pass`] for the purposes of
63 /// reading/writing to the values of [`RwData`]s ensures that no
64 /// panic or invalid borrow happens at runtime, even while working
65 /// with untrusted code. More importantly, Duat uses these
66 /// guarantees in order to give the end user a ridiculous amount
67 /// of freedom in where they can do things, whilst keeping Rust's
68 /// number one rule and ensuring thread safety, even with a
69 /// relatively large amount of shareable state.
70 ///
71 /// # Panics
72 ///
73 /// Panics if there is a mutable borrow of this struct somewhere,
74 /// which could happen if you use [`RwData::write_unsafe`] or
75 /// [`RwData::write_unsafe_as`] from some other place
76 ///
77 /// [`Area`]: crate::ui::RawArea
78 pub fn read<Ret>(&self, pa: &Pass, f: impl FnOnce(&File<U>, &U::Area) -> Ret) -> Ret {
79 if let Some((handle, _)) = self.fixed.as_ref() {
80 f(&handle.widget.acquire(pa), &handle.area)
81 } else {
82 self.current.read(pa, |parts| {
83 let (handle, _) = parts.as_ref().unwrap();
84 f(&handle.widget.acquire(pa), &handle.area)
85 })
86 }
87 }
88
89 /// Writes to the [`File`] and [`Area`] within using a [`Pass`]
90 ///
91 /// The consistent use of a [`Pass`] for the purposes of
92 /// reading/writing to the values of [`RwData`]s ensures that no
93 /// panic or invalid borrow happens at runtime, even while working
94 /// with untrusted code. More importantly, Duat uses these
95 /// guarantees in order to give the end user a ridiculous amount
96 /// of freedom in where they can do things, whilst keeping Rust's
97 /// number one rule and ensuring thread safety, even with a
98 /// relatively large amount of shareable state.
99 ///
100 /// # Panics
101 ///
102 /// Panics if there is any type of borrow of this struct
103 /// somewhere, which could happen if you use
104 /// [`RwData::read_unsafe`] or [`RwData::write_unsafe`], for
105 /// example.
106 ///
107 /// [`Area`]: crate::ui::RawArea
108 pub fn write<Ret>(&self, pa: &mut Pass, f: impl FnOnce(&mut File<U>, &U::Area) -> Ret) -> Ret {
109 if let Some((handle, _)) = self.fixed.as_ref() {
110 f(&mut handle.widget.acquire_mut(pa), &handle.area)
111 } else {
112 // SAFETY: Since the update closure only uses a write method, the
113 // Pass becomes unusable for other purposes, making it impossible
114 // to make further borrows, asserting that there is no other borrow
115 // for self.current.
116 unsafe {
117 self.current.read_unsafe(|parts| {
118 let (handle, _) = parts.as_ref().unwrap();
119 f(&mut handle.widget.acquire_mut(pa), &handle.area)
120 })
121 }
122 }
123 }
124
125 /// Reads a [`Widget`] related to this [`File`], alongside its
126 /// [`Area`], with a [`Pass`]
127 ///
128 /// A related [`Widget`] is one that was pushed to this [`File`]
129 /// during the [`OnFileOpen`] [hook].
130 ///
131 /// [`Area`]: crate::ui::Area
132 /// [`OnFileOpen`]: crate::hook::OnFileOpen
133 /// [hook]: crate::hook
134 pub fn read_related<W: 'static, R>(
135 &self,
136 pa: &Pass,
137 f: impl FnOnce(&W, &U::Area) -> R,
138 ) -> Option<R> {
139 let read = |(handle, related): &FileParts<U>| {
140 if TypeId::of::<W>() == TypeId::of::<File<U>>() {
141 let area = handle.area();
142 handle.widget().read_as(pa, |w| f(w, area))
143 } else {
144 let related = related.acquire(pa);
145 related
146 .iter()
147 .find(|node| node.data_is::<W>())
148 .and_then(|node| node.widget().read_as(pa, |w| f(w, node.area())))
149 }
150 };
151
152 if let Some(parts) = self.fixed.as_ref() {
153 read(parts)
154 } else {
155 self.current.read(pa, |parts| read(parts.as_ref().unwrap()))
156 }
157 }
158
159 /// Gets the [`RwData`] and [`Area`] of a related widget, with a
160 /// [`Pass`]
161 ///
162 /// A related [`Widget`] is one that was pushed to this [`File`]
163 /// during the [`OnFileOpen`] [hook].
164 ///
165 /// [`Area`]: crate::ui::Area
166 /// [`OnFileOpen`]: crate::hook::OnFileOpen
167 /// [hook]: crate::hook
168 pub fn get_related_widget<W: Widget<U> + 'static>(&self, pa: &Pass) -> Option<Handle<W, U>> {
169 let get_related = |(handle, related): &FileParts<U>| {
170 if TypeId::of::<W>() == TypeId::of::<File<U>>() {
171 let widget = handle.widget().try_downcast()?;
172 Some(Handle::from_parts(
173 widget,
174 handle.area().clone(),
175 handle.mask().clone(),
176 ))
177 } else {
178 related.read(pa, |related| {
179 related.iter().find_map(|node| {
180 let (widget, area, mask, _) = node.parts();
181 widget
182 .try_downcast()
183 .map(|data| Handle::from_parts(data, area.clone(), mask.clone()))
184 })
185 })
186 }
187 };
188
189 if let Some(parts) = self.fixed.as_ref() {
190 get_related(parts)
191 } else {
192 self.current
193 .read(pa, |parts| get_related(parts.as_ref().unwrap()))
194 }
195 }
196
197 /// Writes to the related widgets
198 pub(crate) fn write_related_widgets(&self, pa: &mut Pass, f: impl FnOnce(&mut Vec<Node<U>>)) {
199 if let Some((.., related)) = self.fixed.as_ref() {
200 related.write(pa, f)
201 } else {
202 // SAFETY: Same situation as the write method
203 unsafe {
204 self.current
205 .read_unsafe(|parts| parts.as_ref().unwrap().1.write(pa, f))
206 }
207 }
208 }
209
210 ////////// Querying functions
211
212 /// Gets a [`Handle`] from this [`FileHandle`]
213 pub fn handle(&self, pa: &Pass) -> Handle<File<U>, U> {
214 if let Some((handle, _)) = self.fixed.as_ref() {
215 handle.clone()
216 } else {
217 self.current.acquire(pa).as_ref().unwrap().0.clone()
218 }
219 }
220
221 /// Wether someone else called [`write`] or [`write_as`] since the
222 /// last [`read`] or [`write`]
223 ///
224 /// Do note that this *DOES NOT* mean that the value inside has
225 /// actually been changed, it just means a mutable reference was
226 /// acquired after the last call to [`has_changed`].
227 ///
228 /// Some types like [`Text`], and traits like [`Widget`] offer
229 /// [`needs_update`] methods, you should try to determine what
230 /// parts to look for changes.
231 ///
232 /// Generally though, you can use this method to gauge that.
233 ///
234 /// [`write`]: RwData::write
235 /// [`write_as`]: RwData::write_as
236 /// [`read`]: RwData::read
237 /// [`has_changed`]: RwData::has_changed
238 /// [`Text`]: crate::text::Text
239 /// [`Widget`]: crate::ui::Widget
240 /// [`needs_update`]: crate::ui::Widget::needs_update
241 pub fn has_changed(&self) -> bool {
242 if let Some((handle, _)) = self.fixed.as_ref() {
243 handle.has_changed()
244 } else {
245 self.current.has_changed()
246 || self.current.read_raw(|parts| {
247 let (handle, _) = parts.as_ref().unwrap();
248 handle.has_changed()
249 })
250 }
251 }
252
253 /// Wether the [`File`] within has swapped to another
254 ///
255 /// This can only happen when this is a
256 pub fn has_swapped(&self) -> bool {
257 let has_changed = self.current.has_changed();
258 self.current.declare_as_read();
259 has_changed
260 }
261
262 /// Wether the [`RwData`] within and another point to the same
263 /// value
264 pub fn ptr_eq<T: ?Sized>(&self, pa: &Pass, other: &RwData<T>) -> bool {
265 if let Some((handle, ..)) = self.fixed.as_ref() {
266 handle.ptr_eq(other)
267 } else {
268 self.current
269 .read(pa, |parts| parts.as_ref().unwrap().0.ptr_eq(other))
270 }
271 }
272
273 /// The name of the [`File`] in question
274 pub fn name(&self, pa: &Pass) -> String {
275 if let Some((handle, ..)) = self.fixed.as_ref() {
276 handle.read(pa, |f, _| f.name())
277 } else {
278 self.current.read(pa, |parts| {
279 parts.as_ref().unwrap().0.read(pa, |f, _| f.name())
280 })
281 }
282 }
283
284 /// The path of the [`File`] in question
285 pub fn path(&self, pa: &Pass) -> String {
286 if let Some((handle, ..)) = self.fixed.as_ref() {
287 handle.read(pa, |f, _| f.path())
288 } else {
289 self.current.read(pa, |parts| {
290 parts.as_ref().unwrap().0.read(pa, |f, _| f.path())
291 })
292 }
293 }
294
295 /// The path of the [`File`] in question, if it was set
296 pub fn set_path(&self, pa: &Pass) -> Option<String> {
297 if let Some((handle, ..)) = self.fixed.as_ref() {
298 handle.read(pa, |f, _| f.path_set())
299 } else {
300 self.current.read(pa, |parts| {
301 parts.as_ref().unwrap().0.read(pa, |f, _| f.path_set())
302 })
303 }
304 }
305}
306
307/// A handle to a [`Widget`] in Duat
308///
309/// The [`Handle`] lets you do all sorts of edits on a [`Widget`]. You
310/// can, for example, make use of the [`Selection`]s in its [`Text`]
311/// in order to edit the [`Text`] in a very declarative way.
312///
313/// One of the places where this is commonly done is within [`Mode`]s,
314/// where you get access to the [`Handle`] of the currently active
315/// [`Widget`]. Below is a very straightforward [`Mode`]:
316///
317/// ```rust
318/// use duat_core::prelude::*;
319/// /// A very basic example Mode.
320/// #[derive(Clone)]
321/// struct PlacesCharactersAndMoves;
322///
323/// impl<U: Ui> Mode<U> for PlacesCharactersAndMoves {
324/// type Widget = File<U>;
325///
326/// // ...
327/// fn send_key(&mut self, _: &mut Pass, _: KeyEvent, _: Handle<Self::Widget, U>) {
328/// todo!();
329/// }
330/// }
331/// ```
332///
333/// In order to modify the widget, you must implement the
334/// [`Mode::send_key`] method. In it, you receive the following:
335///
336/// - A [`&mut Pass`], which will give you access to all of duat's
337/// shared state;
338/// - The [key] that was sent, may be a [mapped] key.
339/// - The [`Handle`] for a [`Mode::Widget`].
340///
341/// ```rust
342/// use duat_core::prelude::*;
343/// #[derive(Clone)]
344/// struct PlacesCharactersAndMoves;
345/// impl<U: Ui> Mode<U> for PlacesCharactersAndMoves {
346/// type Widget = File<U>;
347///
348/// fn send_key(&mut self, pa: &mut Pass, key: KeyEvent, handle: Handle<File<U>, U>) {
349/// match key {
350/// // actions based on the key pressed
351/// key!(KeyCode::Char('c')) => {
352/// // Do something when the character 'c' is typed.
353/// }
354/// _ => todo!("The remaining keys"),
355/// }
356/// }
357/// }
358/// ```
359///
360/// (You can use the [`key!`] macro in order to match [`KeyEvent`]s).
361///
362/// With the [`Handle`], you can modify [`Text`] in a simplified
363/// way. This is done by two actions, [editing] and [moving]. You
364/// can only do one of these on any number of selections at the same
365/// time.
366///
367/// ```rust
368/// # use duat_core::prelude::*;
369/// # #[derive(Clone)]
370/// # struct PlacesCharactersAndMoves;
371/// impl<U: Ui> Mode<U> for PlacesCharactersAndMoves {
372/// # type Widget = File<U>;
373/// /* ... */
374/// fn send_key(&mut self, pa: &mut Pass, key: KeyEvent, handle: Handle<Self::Widget, U>) {
375/// match key {
376/// key!(KeyCode::Char(c)) => {
377/// handle.edit_all(pa, |mut e| {
378/// e.insert('c');
379/// e.move_hor(1);
380/// });
381/// },
382/// key!(KeyCode::Right, KeyMod::SHIFT) => {
383/// handle.edit_all(pa, |mut e| {
384/// if e.anchor().is_none() {
385/// e.set_anchor();
386/// }
387/// e.move_hor(1);
388/// });
389/// }
390/// key!(KeyCode::Right) => {
391/// handle.edit_all(pa, |mut e| {
392/// e.unset_anchor();
393/// e.move_hor(1);
394/// });
395/// }
396/// _ => todo!("Predictable remaining implementations")
397/// }
398/// }
399/// # }
400/// ```
401///
402/// [`Mode`]: crate::mode::Mode
403/// [`Mode::Widget`]: crate::mode::Mode::Widget
404/// [`&mut Pass`]: Pass
405/// [`PromptLine`]: https://docs.rs/duat-utils/latest/duat_utils/widgets/struct.PromptLine.html
406/// [`Mode::send_key`]: crate::mode::Mode::send_key
407/// [key]: crate::mode::KeyEvent
408/// [mapped]: crate::mode::map
409/// [`read`]: RwData::read
410/// [`write`]: RwData::write
411/// [`U::Area`]: Ui::Area
412/// [`Self::Widget`]: crate::mode::Mode::Widget
413/// [`Some(selections)`]: Some
414/// [`Ui::Area`]: crate::ui::Ui::Area
415/// [commands]: crate::cmd
416/// [`key!`]: crate::mode::key
417/// [`KeyEvent`]: crate::mode::KeyEvent
418/// [editing]: Cursor
419/// [moving]: Cursor
420/// [`Mode`]: crate::mode::Mode
421/// [`U::Area`]: Ui::Area
422#[derive(Debug)]
423pub struct Handle<W: Widget<U> + ?Sized, U: Ui, S = ()> {
424 widget: RwData<W>,
425 area: U::Area,
426 mask: Rc<Cell<&'static str>>,
427 searcher: RefCell<S>,
428}
429
430impl<W: Widget<U> + ?Sized, U: Ui> Handle<W, U> {
431 /// Returns a new instance of a [`Handle<W, U>`]
432 pub(crate) fn from_parts(
433 widget: RwData<W>,
434 area: U::Area,
435 mask: Rc<Cell<&'static str>>,
436 ) -> Self {
437 Self {
438 widget,
439 area,
440 mask,
441 searcher: RefCell::new(()),
442 }
443 }
444}
445
446impl<W: Widget<U> + ?Sized, U: Ui, S> Handle<W, U, S> {
447 /// Reads from the [`Widget`] and the [`Area`] using a [`Pass`]
448 ///
449 /// The consistent use of a [`Pass`] for the purposes of
450 /// reading/writing to the values of [`RwData`]s ensures that no
451 /// panic or invalid borrow happens at runtime, even while working
452 /// with untrusted code. More importantly, Duat uses these
453 /// guarantees in order to give the end user a ridiculous amount
454 /// of freedom in where they can do things, whilst keeping Rust's
455 /// number one rule and ensuring thread safety, even with a
456 /// relatively large amount of shareable state.
457 ///
458 /// # Panics
459 ///
460 /// Panics if there is a mutable borrow of this struct somewhere,
461 /// which could happen if you use [`RwData::write_unsafe`] or
462 /// [`RwData::write_unsafe_as`] from some other place
463 ///
464 /// [`Area`]: crate::ui::RawArea
465 pub fn read<Ret>(&self, pa: &Pass, f: impl FnOnce(&W, &U::Area) -> Ret) -> Ret {
466 f(&self.widget.acquire(pa), &self.area)
467 }
468
469 /// Writes to the [`Widget`] and [`Area`] within using a [`Pass`]
470 ///
471 /// The consistent use of a [`Pass`] for the purposes of
472 /// reading/writing to the values of [`RwData`]s ensures that no
473 /// panic or invalid borrow happens at runtime, even while working
474 /// with untrusted code. More importantly, Duat uses these
475 /// guarantees in order to give the end user a ridiculous amount
476 /// of freedom in where they can do things, whilst keeping Rust's
477 /// number one rule and ensuring thread safety, even with a
478 /// relatively large amount of shareable state.
479 ///
480 /// # Panics
481 ///
482 /// Panics if there is any type of borrow of this struct
483 /// somewhere, which could happen if you use
484 /// [`RwData::read_unsafe`] or [`RwData::write_unsafe`], for
485 /// example.
486 ///
487 /// [`Area`]: crate::ui::RawArea
488 pub fn write<Ret>(&self, pa: &mut Pass, f: impl FnOnce(&mut W, &U::Area) -> Ret) -> Ret {
489 f(&mut self.widget.acquire_mut(pa), &self.area)
490 }
491
492 ////////// Selection Editing functions
493
494 /// Edits the nth [`Selection`] in the [`Text`]
495 ///
496 /// Once dropped, the [`Selection`] in this [`Cursor`] will be
497 /// added back to the list of [`Selection`]s, unless it is
498 /// [destroyed]
499 ///
500 /// If you want to edit on the main selection, see [`edit_main`],
501 /// if you want to edit on many [`Selection`]s, see
502 /// [`edit_iter`].
503 ///
504 /// Just like all other `edit` methods, this one will populate the
505 /// [`Selections`], so if there are no [`Selection`]s, it will
506 /// create one at [`Point::default`].
507 ///
508 /// [destroyed]: Cursor::destroy
509 /// [`edit_main`]: Self::edit_main
510 /// [`edit_iter`]: Self::edit_iter
511 /// [`Point::default`]: crate::text::Point::default
512 pub fn edit_nth<Ret>(
513 &self,
514 pa: &mut Pass,
515 n: usize,
516 edit: impl FnOnce(Cursor<W, U::Area, S>) -> Ret,
517 ) -> Ret {
518 fn get_parts<'a, W: Widget<U> + ?Sized, U: Ui>(
519 pa: &mut Pass,
520 widget: &'a RwData<W>,
521 n: usize,
522 ) -> (Selection, bool, RefMut<'a, W>) {
523 let mut widget = widget.acquire_mut(pa);
524 let selections = widget.text_mut().selections_mut().unwrap();
525 selections.populate();
526 let Some((selection, was_main)) = selections.remove(n) else {
527 panic!("Selection index {n} out of bounds");
528 };
529
530 (selection, was_main, widget)
531 }
532
533 let (selection, was_main, mut widget) = get_parts(pa, &self.widget, n);
534
535 // This is safe because of the &mut Pass argument
536 let mut searcher = self.searcher.borrow_mut();
537
538 edit(Cursor::new(
539 selection,
540 n,
541 was_main,
542 &mut *widget,
543 &self.area,
544 None,
545 &mut searcher,
546 ))
547 }
548
549 /// Edits the main [`Selection`] in the [`Text`]
550 ///
551 /// Once dropped, the [`Selection`] in this [`Cursor`] will be
552 /// added back to the list of [`Selection`]s, unless it is
553 /// [destroyed]
554 ///
555 /// If you want to edit on the `nth` selection, see [`edit_nth`],
556 /// same for [`edit_last`], if you want to edit on many
557 /// [`Selection`]s, see [`edit_iter`].
558 ///
559 /// Just like all other `edit` methods, this one will populate the
560 /// [`Selections`], so if there are no [`Selection`]s, it will
561 /// create one at [`Point::default`].
562 ///
563 /// [destroyed]: Cursor::destroy
564 /// [`edit_nth`]: Self::edit_nth
565 /// [`edit_last`]: Self::edit_last
566 /// [`edit_iter`]: Self::edit_iter
567 /// [`Point::default`]: crate::text::Point::default
568 pub fn edit_main<Ret>(
569 &self,
570 pa: &mut Pass,
571 edit: impl FnOnce(Cursor<W, U::Area, S>) -> Ret,
572 ) -> Ret {
573 self.edit_nth(
574 pa,
575 self.widget
576 .read(pa, |wid| wid.text().selections().unwrap().main_index()),
577 edit,
578 )
579 }
580
581 /// Edits the last [`Selection`] in the [`Text`]
582 ///
583 /// Once dropped, the [`Selection`] in this [`Cursor`] will be
584 /// added back to the list of [`Selection`]s, unless it is
585 /// [destroyed]
586 ///
587 /// If you want to edit on the `nth` selection, see [`edit_nth`],
588 /// same for [`edit_main`], if you want to edit on many
589 /// [`Selection`]s, see [`edit_iter`].
590 ///
591 /// Just like all other `edit` methods, this one will populate the
592 /// [`Selections`], so if there are no [`Selection`]s, it will
593 /// create one at [`Point::default`].
594 ///
595 /// [destroyed]: Cursor::destroy
596 /// [`edit_nth`]: Self::edit_nth
597 /// [`edit_main`]: Self::edit_main
598 /// [`edit_iter`]: Self::edit_iter
599 /// [`Point::default`]: crate::text::Point::default
600 pub fn edit_last<Ret>(
601 &self,
602 pa: &mut Pass,
603 edit: impl FnOnce(Cursor<W, U::Area, S>) -> Ret,
604 ) -> Ret {
605 self.edit_nth(
606 pa,
607 self.widget
608 .read(pa, |wid| wid.text().selections().unwrap().len())
609 .saturating_sub(1),
610 edit,
611 )
612 }
613
614 /// A [`Lender`] over all [`Cursor`]s of the [`Text`]
615 ///
616 /// This lets you easily iterate over all [`Selection`]s, without
617 /// having to worry about insertion affecting the order at which
618 /// they are edited (like what repeated calls to [`edit_nth`]
619 /// would do)
620 ///
621 /// Note however that you can't use a [`Lender`] (also known as a
622 /// lending iterator) in a `for` loop, but you should be able
623 /// to just `while let Some(e) = editors.next() {}` or
624 /// `handle.edit_iter().for_each(|_| {})` instead.
625 ///
626 /// Just like all other `edit` methods, this one will populate the
627 /// [`Selections`], so if there are no [`Selection`]s, it will
628 /// create one at [`Point::default`].
629 ///
630 /// [`edit_nth`]: Self::edit_nth
631 /// [`Point::default`]: crate::text::Point::default
632 pub fn edit_iter<Ret>(
633 &self,
634 pa: &mut Pass,
635 edit: impl FnOnce(Cursors<'_, W, U::Area, S>) -> Ret,
636 ) -> Ret {
637 edit(self.get_iter(pa))
638 }
639
640 /// A shortcut for iterating over all selections
641 ///
642 /// This is the equivalent of calling:
643 ///
644 /// ```rust
645 /// # use duat_core::prelude::*;
646 /// # fn test<U: Ui>(pa: &mut Pass, handle: Handle<File<U>, U, ()>) {
647 /// handle.edit_iter(pa, |iter| iter.for_each(|e| { /* .. */ }));
648 /// # }
649 /// ```
650 ///
651 /// But it can't return a value, and is meant to reduce the
652 /// indentation that will inevitably come from using the
653 /// equivalent long form call.
654 pub fn edit_all(&self, pa: &mut Pass, edit: impl FnMut(Cursor<W, U::Area, S>)) {
655 self.get_iter(pa).for_each(edit);
656 }
657
658 fn get_iter(&self, pa: &mut Pass) -> Cursors<'_, W, U::Area, S> {
659 let mut widget = self.widget.acquire_mut(pa);
660 let selections = widget.text_mut().selections_mut().unwrap();
661 selections.populate();
662
663 let searcher = self.searcher.borrow_mut();
664
665 Cursors::new(0, widget, &self.area, searcher)
666 }
667
668 ////////// Functions derived from RwData
669
670 /// Reads the [`Text`] of the [`Widget`]
671 pub fn read_text<Ret>(&self, pa: &Pass, read: impl FnOnce(&Text) -> Ret) -> Ret {
672 let widget = self.widget.acquire(pa);
673 read(widget.text())
674 }
675
676 /// Writes to the [`Text`] of the [`Widget`]
677 pub fn write_text<Ret>(&self, pa: &mut Pass, write: impl FnOnce(&mut Text) -> Ret) -> Ret {
678 let mut widget = self.widget.acquire_mut(pa);
679 write(widget.text_mut())
680 }
681
682 /// Reads the [`Selections`] of the [`Widget`]
683 pub fn read_selections<Ret>(&self, pa: &Pass, read: impl FnOnce(&Selections) -> Ret) -> Ret {
684 let widget = self.widget.acquire(pa);
685 read(widget.text().selections().unwrap())
686 }
687
688 /// Writes to the [`Selections`] of the [`Widget`]
689 pub fn write_selections<Ret>(
690 &self,
691 pa: &mut Pass,
692 write: impl FnOnce(&mut Selections) -> Ret,
693 ) -> Ret {
694 let mut widget = self.widget.acquire_mut(pa);
695 write(widget.text_mut().selections_mut().unwrap())
696 }
697
698 ////////// Direct Text manipulation
699
700 /// Clones the [`Text`] within
701 pub fn clone_text(&self, pa: &Pass) -> Text {
702 self.widget.clone_text(pa)
703 }
704
705 /// Replaces the [`Text`] within with a [`Default`] version
706 pub fn take_text(&self, pa: &mut Pass) -> Text {
707 self.widget.take_text(pa)
708 }
709
710 /// Replaces the [`Text`] of the [`Widget`], returning the
711 /// previous value
712 pub fn replace_text(&self, pa: &mut Pass, text: impl Into<Text>) -> Text {
713 self.widget.replace_text(pa, text.into())
714 }
715
716 /// Undoes the last moment in the history, if there is one
717 pub fn undo(&self, pa: &mut Pass) {
718 self.widget.write(pa, |wid| wid.text_mut().undo());
719 }
720
721 /// Redoes the last moment in the history, if there is one
722 pub fn redo(&self, pa: &mut Pass) {
723 self.widget.write(pa, |wid| wid.text_mut().redo());
724 }
725
726 /// Finishes the current moment and adds a new one to the history
727 pub fn new_moment(&self, pa: &mut Pass) {
728 self.widget.write(pa, |wid| wid.text_mut().new_moment());
729 }
730
731 ////////// Area functions
732
733 /// Scrolls the [`Text`] veritcally by an amount
734 ///
735 /// If [`PrintCfg.allow_overscroll`] is set, then the [`Text`]
736 /// will be allowed to scroll beyond the last line, up until
737 /// reaching the `scrolloff.y` value.
738 ///
739 /// [`PrintCfg.allow_overscroll`]: crate::cfg::PrintCfg::allow_overscroll
740 pub fn scroll_ver(&self, pa: &Pass, dist: i32) {
741 let widget = self.widget.acquire(pa);
742 self.area()
743 .scroll_ver(widget.text(), dist, widget.print_cfg());
744 self.widget.declare_written();
745 }
746
747 /// Scrolls the [`Text`] to the visual line of a [`TwoPoints`]
748 ///
749 /// If `scroll_beyond` is set, then the [`Text`] will be allowed
750 /// to scroll beyond the last line, up until reaching the
751 /// `scrolloff.y` value.
752 pub fn scroll_to_points(&self, pa: &Pass, points: impl TwoPoints) {
753 let widget = self.widget.acquire(pa);
754 self.area
755 .scroll_to_points(widget.text(), points, widget.print_cfg());
756 self.widget.declare_written();
757 }
758
759 /// The start points that should be printed
760 pub fn start_points(&self, pa: &Pass) -> (Point, Option<Point>) {
761 let widget = self.widget.acquire(pa);
762 self.area.start_points(widget.text(), widget.print_cfg())
763 }
764
765 /// The end points that should be printed
766 pub fn end_points(&self, pa: &Pass) -> (Point, Option<Point>) {
767 let widget = self.widget.acquire(pa);
768 self.area.end_points(widget.text(), widget.print_cfg())
769 }
770
771 ////////// Querying functions
772
773 /// This [`Handle`]'s [`Widget`]
774 pub fn widget(&self) -> &RwData<W> {
775 &self.widget
776 }
777
778 /// This [`Handle`]'s [`U::Area`]
779 ///
780 /// [`U::Area`]: crate::ui::Ui::Area
781 pub fn area(&self) -> &U::Area {
782 &self.area
783 }
784
785 /// Gets this [`Handle`]'s mask
786 ///
787 /// This mask is going to be used to map [`Form`]s to other
788 /// [`Form`]s when printing via [`Widget::print`]. To see more
789 /// about how masks work, see [`form::enable_mask`].
790 ///
791 /// [`Form`]: crate::form::Form
792 /// [`form::enable_mask`]: crate::form::enable_mask
793 pub fn mask(&self) -> &Rc<Cell<&'static str>> {
794 &self.mask
795 }
796
797 /// Sets this [`Handle`]'s mask, returning the previous one
798 ///
799 /// This mask is going to be used to map [`Form`]s to other
800 /// [`Form`]s when printing via [`Widget::print`]. To see more
801 /// about how masks work, see [`form::enable_mask`].
802 ///
803 /// [`Form`]: crate::form::Form
804 /// [`form::enable_mask`]: crate::form::enable_mask
805 pub fn set_mask(&self, mask: &'static str) -> &'static str {
806 self.widget.declare_written();
807 self.mask.replace(mask)
808 }
809
810 /// Wether someone else called [`write`] or [`write_as`] since the
811 /// last [`read`] or [`write`]
812 ///
813 /// Do note that this *DOES NOT* mean that the value inside has
814 /// actually been changed, it just means a mutable reference was
815 /// acquired after the last call to [`has_changed`].
816 ///
817 /// Some types like [`Text`], and traits like [`Widget`] offer
818 /// [`needs_update`] methods, you should try to determine what
819 /// parts to look for changes.
820 ///
821 /// Generally though, you can use this method to gauge that.
822 ///
823 /// [`write`]: RwData::write
824 /// [`write_as`]: RwData::write_as
825 /// [`read`]: RwData::read
826 /// [`has_changed`]: RwData::has_changed
827 /// [`Text`]: crate::text::Text
828 /// [`Widget`]: crate::ui::Widget
829 /// [`needs_update`]: crate::ui::Widget::needs_update
830 pub fn has_changed(&self) -> bool {
831 self.widget.has_changed() || self.area.has_changed()
832 }
833
834 /// Wether the [`RwData`] within and another point to the same
835 /// value
836 pub fn ptr_eq<T: ?Sized>(&self, other: &RwData<T>) -> bool {
837 self.widget.ptr_eq(other)
838 }
839
840 /// The [`Widget`]'s [`PrintCfg`]
841 pub fn cfg(&self, pa: &Pass) -> PrintCfg {
842 self.widget.read(pa, Widget::print_cfg)
843 }
844
845 /// Attaches a [`Searcher`] to this [`Handle`], so you can do
846 /// incremental search
847 ///
848 /// An [`Handle`] with a [`Searcher`] not only has its usual
849 /// editing capabilities, but is also able to act on requested
850 /// regex searches, like those from [`IncSearch`], in
851 /// [`duat-utils`]. This means that a user can type up a
852 /// [prompt] to search for something, and the [`Handle`]
853 /// can use the [`Searcher`] to interpret how that search will
854 /// be utilized. Examples of this can be found in the
855 /// [`duat-utils`] crate, as well as the [`duat-kak`] crate,
856 /// which has some more advanced usage.
857 ///
858 /// [`Searcher`]: crate::text::Searcher
859 /// [`Selection`]: crate::mode::Selection
860 /// [`Cursor`]: crate::mode::Cursor
861 /// [`IncSearch`]: https://docs.rs/duat-utils/latest/duat_utils/modes/struct.IncSearch.html
862 /// [`duat-utils`]: https://docs.rs/duat-utils/lastest/
863 /// [prompt]: https://docs.rs/duat-utils/latest/duat_utils/modes/trait.PromptMode.html
864 /// [`duat-kak`]: https://docs.rs/duat-kak/lastest/
865 pub fn attach_searcher(&self, searcher: Searcher) -> Handle<W, U, Searcher> {
866 Handle {
867 widget: self.widget.clone(),
868 area: self.area.clone(),
869 mask: self.mask.clone(),
870 searcher: RefCell::new(searcher),
871 }
872 }
873}
874
875impl<W: Widget<U> + ?Sized, U: Ui, S: Clone> Clone for Handle<W, U, S> {
876 fn clone(&self) -> Self {
877 Self {
878 widget: self.widget.clone(),
879 area: self.area.clone(),
880 mask: self.mask.clone(),
881 searcher: self.searcher.clone(),
882 }
883 }
884}