use std::sync::{
Arc,
atomic::{AtomicBool, Ordering},
};
use crate::{
buffer::Buffer,
context::Handle,
data::{Pass, RwData},
form,
hook::{self, BufferPrinted, FocusedOn, OnMouseEvent, UnfocusedFrom},
mode::{ToggleEvent, TwoPointsPlace},
opts::PrintOpts,
session::UiMouseEvent,
text::{Text, TextMut},
ui::{PrintInfo, RwArea},
};
#[allow(unused)]
pub trait Widget: Send + 'static {
fn text(&self) -> &Text;
fn text_mut(&mut self) -> TextMut<'_>;
fn print_opts(&self) -> PrintOpts {
PrintOpts::new()
}
}
#[derive(Clone)]
pub(crate) struct Node {
handle: Handle<dyn Widget>,
print: Arc<dyn Fn(&mut Pass, &Handle<dyn Widget>) + Send + Sync>,
on_focus: Arc<dyn Fn(&mut Pass, Handle<dyn Widget>) + Send + Sync>,
on_unfocus: Arc<dyn Fn(&mut Pass, Handle<dyn Widget>) + Send + Sync>,
on_mouse_event: Arc<dyn Fn(&mut Pass, UiMouseEvent) + Send + Sync>,
}
impl Node {
pub(crate) fn new<W: Widget>(
widget: RwData<W>,
area: RwArea,
master: Option<Handle<dyn Widget>>,
is_closed: Arc<AtomicBool>,
) -> Self {
Self::from_handle(Handle::new(widget, area, master, is_closed))
}
pub(crate) fn from_handle<W: Widget>(handle: Handle<W>) -> Self {
Self {
handle: handle.to_dyn(),
print: if let Some(buffer) = handle.try_downcast::<Buffer>() {
let handle = handle.clone();
Arc::new(move |pa, orig_handle| {
Buffer::update(pa, &buffer);
handle.area.print(
pa,
handle.text(pa),
handle.opts(pa),
form::painter_with_widget::<W>(),
);
hook::trigger(pa, BufferPrinted(buffer.clone()));
orig_handle.declare_as_read();
orig_handle.area().0.declare_as_read();
})
} else {
let handle = handle.clone();
Arc::new(move |pa, _| {
handle.area.print(
pa,
handle.text(pa),
handle.opts(pa),
form::painter_with_widget::<W>(),
);
})
},
on_focus: Arc::new({
let handle = handle.clone();
move |pa, old| _ = hook::trigger(pa, FocusedOn((old, handle.clone())))
}),
on_unfocus: Arc::new({
let handle = handle.clone();
move |pa, new| _ = hook::trigger(pa, UnfocusedFrom((handle.clone(), new)))
}),
on_mouse_event: Arc::new({
let handle = handle.clone();
let dyn_handle = handle.to_dyn();
move |pa, event| {
let opts = handle.opts(pa);
let text = handle.text(pa);
let points = handle.area().points_at_coord(pa, text, event.coord, opts);
hook::trigger(pa, OnMouseEvent {
handle: dyn_handle.clone(),
points,
coord: event.coord,
kind: event.kind,
modifiers: event.modifiers,
});
hook::trigger(pa, OnMouseEvent {
handle: handle.clone(),
points,
coord: event.coord,
kind: event.kind,
modifiers: event.modifiers,
});
if let Some(TwoPointsPlace::Within(points)) = points {
let event = ToggleEvent {
handle: &dyn_handle,
points,
coord: event.coord,
kind: event.kind,
modifiers: event.modifiers,
};
let toggles = handle.text(pa).toggles_surrounding(points.real);
crate::utils::catch_panic(|| {
for (range, toggle_fn) in toggles {
toggle_fn.lock().unwrap()(pa, event, range);
}
});
}
}
}),
}
}
pub(crate) fn read_as<'a, W: Widget>(&'a self, pa: &'a Pass) -> Option<&'a W> {
self.handle.read_as(pa)
}
pub(crate) fn widget(&self) -> &RwData<dyn Widget> {
self.handle.widget()
}
pub(crate) fn area(&self) -> &RwArea {
self.handle.area()
}
pub(crate) fn try_downcast<W: Widget>(&self) -> Option<Handle<W>> {
self.handle.try_downcast()
}
pub(crate) fn handle(&self) -> &Handle<dyn Widget> {
&self.handle
}
pub(crate) fn data_is<W: 'static>(&self) -> bool {
self.handle.widget().is::<W>()
}
pub(crate) fn ptr_eq<W: ?Sized>(&self, other: &RwData<W>) -> bool {
self.handle.ptr_eq(other)
}
pub(crate) fn needs_update(&self, pa: &Pass) -> bool {
self.handle.update_requested.load(Ordering::Relaxed)
|| self.handle.widget().has_changed()
|| self.handle.area.has_changed(pa)
}
pub(crate) fn print(&self, pa: &mut Pass, win: usize) {
self.handle.update_requested.store(false, Ordering::Relaxed);
crate::context::windows().cleanup_despawned(pa);
if self.handle().is_closed() {
return;
}
let print_info = self.handle.area().get_print_info(pa);
let (widget, _) = self.handle.write_with_area(pa);
if print_info != PrintInfo::default() {
widget.text_mut().update_bounds();
}
let widgets_to_spawn = widget.text_mut().get_widget_spawns();
for (_, spawn) in widgets_to_spawn {
spawn(pa, win, self.handle.clone());
}
(self.print)(pa, &self.handle);
}
pub(crate) fn on_focus(&self, pa: &mut Pass, old: Handle<dyn Widget>) {
self.handle.area().set_as_active(pa);
(self.on_focus)(pa, old)
}
pub(crate) fn on_unfocus(&self, pa: &mut Pass, new: Handle<dyn Widget>) {
(self.on_unfocus)(pa, new)
}
pub(crate) fn on_mouse_event(&self, pa: &mut Pass, mouse_event: UiMouseEvent) {
(self.on_mouse_event)(pa, mouse_event);
}
}
impl std::fmt::Debug for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Node")
.field("handle", &self.handle)
.finish_non_exhaustive()
}
}