use std::sync::{
Arc,
atomic::{AtomicBool, Ordering},
};
use crate::{
context,
data::{Pass, RwData, WriteableTuple},
mode::{Cursor, ModSelection, Selection, Selections},
opts::PrintOpts,
text::{Text, TextMut, TextParts, TwoPoints},
ui::{Area, DynSpawnSpecs, PushSpecs, RwArea, Widget},
};
pub struct Handle<W: ?Sized = crate::buffer::Buffer> {
widget: RwData<W>,
pub(crate) area: RwArea,
related: RwData<Vec<(Handle<dyn Widget>, WidgetRelation)>>,
is_closed: Arc<AtomicBool>,
pub(crate) update_requested: Arc<AtomicBool>,
}
impl<W: Widget + ?Sized> Handle<W> {
pub(crate) fn new(
widget: RwData<W>,
area: RwArea,
main: Option<Handle<dyn Widget>>,
is_closed: Arc<AtomicBool>,
) -> Self {
Self {
widget,
area,
related: RwData::new(
main.map(|handle| (handle, WidgetRelation::Main))
.into_iter()
.collect(),
),
is_closed,
update_requested: Arc::new(AtomicBool::new(false)),
}
}
}
impl<W: 'static + ?Sized> Handle<W> {
pub fn read<'a>(&'a self, pa: &'a Pass) -> &'a W {
self.widget.read(pa)
}
pub fn read_as<'a, W2: Widget>(&'a self, pa: &'a Pass) -> Option<&'a W2> {
self.widget.read_as(pa)
}
pub fn declare_as_read(&self) {
self.widget.declare_as_read();
}
pub fn write<'a>(&'a self, pa: &'a mut Pass) -> &'a mut W {
self.widget.write(pa)
}
pub fn write_with_area<'p>(&'p self, pa: &'p mut Pass) -> (&'p mut W, &'p mut Area) {
pa.write_many((&self.widget, &self.area.0))
}
pub fn write_then<'p, Tup: WriteableTuple<'p, impl std::any::Any>>(
&'p self,
pa: &'p mut Pass,
tup_fn: impl FnOnce(&'p W) -> Tup,
) -> (&'p mut W, Tup::Return) {
self.widget.write_then(pa, tup_fn)
}
pub fn declare_written(&self) {
self.widget.declare_written();
}
pub fn try_downcast<W2: Widget>(&self) -> Option<Handle<W2>> {
Some(Handle {
widget: self.widget.try_downcast()?,
area: self.area.clone(),
related: self.related.clone(),
is_closed: self.is_closed.clone(),
update_requested: self.update_requested.clone(),
})
}
pub fn widget(&self) -> &RwData<W> {
&self.widget
}
pub fn area(&self) -> &RwArea {
&self.area
}
pub fn has_changed(&self, pa: &Pass) -> bool {
self.widget.has_changed() || self.area.has_changed(pa)
}
pub fn ptr_eq<T: ?Sized>(&self, other: &RwData<T>) -> bool {
self.widget.ptr_eq(other)
}
pub fn request_update(&self) {
self.update_requested.store(true, Ordering::Relaxed);
}
pub fn master(&self, pa: &Pass) -> Option<Handle<dyn Widget>> {
self.related.read(pa).iter().find_map(|(handle, relation)| {
(*relation == WidgetRelation::Main).then_some(handle.clone())
})
}
pub fn buffer(&self, pa: &Pass) -> Option<Handle> {
self.related.read(pa).iter().find_map(|(handle, relation)| {
handle
.try_downcast()
.filter(|_| *relation == WidgetRelation::Main)
})
}
pub fn read_related<'a, W2: Widget>(
&'a self,
pa: &'a Pass,
) -> impl Iterator<Item = (&'a W2, &'a Area, WidgetRelation)> {
self.read_as(pa)
.map(|w| (w, self.area().read(pa), WidgetRelation::Main))
.into_iter()
.chain(self.related.read(pa).iter().filter_map(|(handle, rel)| {
handle
.read_as(pa)
.map(|w| (w, handle.area().read(pa), *rel))
}))
}
pub fn get_related<'a, W2: Widget>(
&'a self,
pa: &'a Pass,
) -> Vec<(Handle<W2>, WidgetRelation)> {
self.related
.read(pa)
.iter()
.filter_map(|(handle, rel)| handle.try_downcast().zip(Some(*rel)))
.collect()
}
pub(crate) fn related(&self) -> &RwData<Vec<(Handle<dyn Widget>, WidgetRelation)>> {
&self.related
}
pub fn is_closed(&self) -> bool {
self.is_closed.load(Ordering::Relaxed)
}
pub(crate) fn declare_closed(&self) {
self.is_closed.store(true, Ordering::Relaxed);
}
}
impl<W: Widget + ?Sized> Handle<W> {
pub fn text<'p>(&'p self, pa: &'p Pass) -> &'p Text {
self.read(pa).text()
}
pub fn text_mut<'p>(&'p self, pa: &'p mut Pass) -> TextMut<'p> {
self.write(pa).text_mut()
}
pub fn text_parts<'p>(&'p self, pa: &'p mut Pass) -> TextParts<'p> {
self.write(pa).text_mut().parts()
}
pub fn selections<'p>(&'p self, pa: &'p Pass) -> &'p Selections {
self.read(pa).text().selections()
}
pub fn selections_mut<'p>(&'p self, pa: &'p mut Pass) -> &'p mut Selections {
self.write(pa).text_mut().selections_mut()
}
pub fn edit_nth<Ret>(
&self,
pa: &mut Pass,
n: usize,
edit: impl FnOnce(Cursor<W>) -> Ret,
) -> Ret {
fn get_parts<'a, W: Widget + ?Sized>(
pa: &'a mut Pass,
handle: &'a Handle<W>,
n: usize,
) -> (Selection, bool, &'a mut W, &'a Area) {
let (widget, area) = handle.write_with_area(pa);
let selections = widget.text_mut().selections_mut();
selections.populate();
let Some((selection, was_main)) = selections.remove(n) else {
panic!("Selection index {n} out of bounds");
};
(selection, was_main, widget, area)
}
let (selection, was_main, widget, area) = get_parts(pa, self, n);
let mut selections = vec![Some(ModSelection::new(selection, n, was_main))];
let ret = edit(Cursor::new(&mut selections, 0, (widget, area), None));
crate::mode::reinsert_selections(selections.into_iter().flatten(), widget, None);
ret
}
pub fn edit_main<Ret>(&self, pa: &mut Pass, edit: impl FnOnce(Cursor<W>) -> Ret) -> Ret {
self.edit_nth(
pa,
self.widget.read(pa).text().selections().main_index(),
edit,
)
}
pub fn edit_last<Ret>(&self, pa: &mut Pass, edit: impl FnOnce(Cursor<W>) -> Ret) -> Ret {
let len = self.widget.read(pa).text().selections().len();
self.edit_nth(pa, len.saturating_sub(1), edit)
}
pub fn edit_all(&self, pa: &mut Pass, edit: impl FnMut(Cursor<W>)) {
let (widget, area) = self.write_with_area(pa);
widget.text_mut().selections_mut().populate();
crate::mode::on_each_cursor(widget, area, edit);
}
pub fn scroll_ver(&self, pa: &mut Pass, dist: i32) {
let (widget, area) = self.write_with_area(pa);
area.scroll_ver(widget.text(), dist, widget.print_opts());
self.widget.declare_written();
}
pub fn scroll_to_points(&self, pa: &mut Pass, points: TwoPoints) {
let (widget, area) = self.write_with_area(pa);
area.scroll_to_points(widget.text(), points, widget.print_opts());
self.widget.declare_written();
}
pub fn start_points(&self, pa: &Pass) -> TwoPoints {
let widget = self.widget.read(pa);
self.area
.start_points(pa, widget.text(), widget.print_opts())
}
pub fn end_points(&self, pa: &Pass) -> TwoPoints {
let widget = self.widget.read(pa);
self.area.end_points(pa, widget.text(), widget.print_opts())
}
pub fn opts(&self, pa: &Pass) -> PrintOpts {
self.widget.read(pa).print_opts()
}
pub fn close(&self, pa: &mut Pass) -> Result<(), Text> {
context::windows().close(pa, self)
}
}
impl<W: Widget> Handle<W> {
pub fn push_inner_widget<PW: Widget>(
&self,
pa: &mut Pass,
widget: PW,
specs: PushSpecs,
) -> Handle<PW> {
let main = if let Some((main, _)) = self
.related
.read(pa)
.iter()
.find(|(_, relation)| *relation == WidgetRelation::Main)
{
main.clone()
} else {
self.to_dyn()
};
let handle = context::windows()
.push_widget(pa, (&self.area, None, specs), widget, Some(&self.area))
.unwrap();
main.related
.write(pa)
.push((handle.to_dyn(), WidgetRelation::Pushed));
handle
}
pub fn push_outer_widget<PW: Widget>(
&self,
pa: &mut Pass,
widget: PW,
specs: PushSpecs,
) -> Handle<PW> {
let main = if let Some((main, _)) = self
.related
.read(pa)
.iter()
.find(|(_, relation)| *relation == WidgetRelation::Main)
{
main.clone()
} else {
self.to_dyn()
};
let handle = if let Some(master) = self.area.get_cluster_master(pa) {
context::windows()
.push_widget(pa, (&master, None, specs), widget, Some(main.area()))
.unwrap()
} else {
context::windows()
.push_widget(pa, (&self.area, None, specs), widget, Some(main.area()))
.unwrap()
};
main.related
.write(pa)
.push((handle.to_dyn(), WidgetRelation::Pushed));
handle
}
pub fn spawn_widget<SW: Widget>(
&self,
pa: &mut Pass,
widget: SW,
specs: DynSpawnSpecs,
) -> Option<Handle<SW>> {
let self_handle = self.to_dyn();
context::windows().spawn_on_widget(pa, (&self.area, specs), widget, move |pa, handle| {
let related = self_handle.related.write(pa);
related.push((handle.clone(), WidgetRelation::Spawned));
if let Some((main, _)) = related
.iter_mut()
.find(|(_, relation)| *relation == WidgetRelation::Main)
.cloned()
{
main.related
.write(pa)
.push((handle.clone(), WidgetRelation::Spawned));
handle.related.write(pa).push((main, WidgetRelation::Main));
} else {
handle
.related
.write(pa)
.push((self_handle, WidgetRelation::Main));
}
})
}
pub fn to_dyn(&self) -> Handle<dyn Widget> {
Handle {
widget: self.widget.to_dyn_widget(),
area: self.area.clone(),
related: self.related.clone(),
is_closed: self.is_closed.clone(),
update_requested: self.update_requested.clone(),
}
}
}
unsafe impl<W: ?Sized> Send for Handle<W> {}
unsafe impl<W: ?Sized> Sync for Handle<W> {}
impl<W1: ?Sized, W2: ?Sized> PartialEq<Handle<W2>> for Handle<W1> {
fn eq(&self, other: &Handle<W2>) -> bool {
self.widget.ptr_eq(&other.widget)
}
}
impl<W: ?Sized> Clone for Handle<W> {
fn clone(&self) -> Self {
Self {
widget: self.widget.clone(),
area: self.area.clone(),
related: self.related.clone(),
is_closed: self.is_closed.clone(),
update_requested: self.update_requested.clone(),
}
}
}
impl<W: ?Sized> std::fmt::Debug for Handle<W> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Handle").finish_non_exhaustive()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WidgetRelation {
Main,
Pushed,
Spawned,
}