use std::{
iter::Chain,
path::Path,
sync::{Arc, Mutex, atomic::AtomicBool},
};
use super::{Node, Widget, layout::Layout};
use crate::{
buffer::{Buffer, BufferOpts, PathKind},
context::{self, Handle},
data::{Pass, RwData},
hook::{self, BufferClosed, BufferSwitched, WidgetOpened, WindowOpened},
mode,
session::UiMouseEvent,
text::{Text, txt},
ui::{Coord, DynSpawnSpecs, PushSpecs, RwArea, SpawnId, StaticSpawnSpecs, Ui},
};
pub struct Windows {
inner: RwData<InnerWindows>,
spawns_to_remove: Mutex<Vec<SpawnId>>,
ui: Ui,
}
struct InnerWindows {
layout: Box<Mutex<dyn Layout>>,
list: Vec<Window>,
new_additions: Arc<Mutex<Option<Vec<(usize, Node)>>>>,
cur_buffer: RwData<Handle>,
cur_widget: RwData<Node>,
cur_win: usize,
buffer_history: BufferHistory,
}
impl Windows {
pub(crate) fn initialize(
pa: &mut Pass,
buffer: Buffer,
layout: Box<Mutex<dyn Layout>>,
ui: Ui,
) {
let new_additions = Arc::new(Mutex::default());
let (window, node) = Window::new(0, pa, ui, buffer, new_additions.clone());
context::set_windows(Self {
inner: RwData::new(InnerWindows {
layout,
list: vec![window.clone()],
new_additions,
cur_buffer: RwData::new(node.try_downcast().unwrap()),
cur_widget: RwData::new(node.clone()),
cur_win: 0,
buffer_history: BufferHistory::default(),
}),
spawns_to_remove: Mutex::new(Vec::new()),
ui,
});
hook::trigger(pa, WindowOpened(window));
hook::trigger(
pa,
WidgetOpened(node.handle().try_downcast::<Buffer>().unwrap()),
);
}
pub(crate) fn new_window(&self, pa: &mut Pass, buffer: Buffer) -> Node {
let win = self.inner.read(pa).list.len();
let new_additions = self.inner.read(pa).new_additions.clone();
let (window, node) = Window::new(win, pa, self.ui, buffer, new_additions);
let inner = self.inner.write(pa);
inner.list.push(window);
hook::trigger(pa, WindowOpened(self.inner.read(pa).list[win].clone()));
hook::trigger(
pa,
WidgetOpened(node.handle().try_downcast::<Buffer>().unwrap()),
);
node
}
pub(crate) fn push_widget<W: Widget>(
&self,
pa: &mut Pass,
(target, on_buffers, specs): (&RwArea, Option<bool>, PushSpecs),
widget: W,
master: Option<&RwArea>,
) -> Option<Handle<W>> {
self.push(pa, (target, on_buffers, specs), widget, master)?
.handle()
.try_downcast()
}
pub(crate) fn spawn_on_widget<W: Widget>(
&self,
pa: &mut Pass,
(target, specs): (&RwArea, DynSpawnSpecs),
widget: W,
callback: impl FnOnce(&mut Pass, Handle<dyn Widget>),
) -> Option<Handle<W>> {
let (win, cluster_master, master) =
self.inner
.read(pa)
.list
.iter()
.enumerate()
.find_map(|(win, window)| {
let inner = window.0.read(pa);
let master = window.nodes(pa).find_map(|node| {
node.area().is_eq(pa, target).then(|| node.handle().clone())
});
if inner.master_area.is_master_of(pa, target) {
Some((win, None, master))
} else if let Some((_, node)) = inner
.spawned
.iter()
.find(|(_, node)| node.area().is_master_of(pa, target))
{
Some((win, node.area().get_cluster_master(pa), master))
} else {
None
}
})?;
let widget = RwData::new(widget);
let id = SpawnId::new();
let path = widget
.read_as::<Buffer>(pa)
.and_then(|buffer| buffer.path_set());
let spawned = cluster_master.as_ref().unwrap_or(target).spawn(
pa,
path.as_ref().map(|p| p.as_ref()),
id,
specs,
)?;
let node = Node::new(widget, spawned, master, Arc::new(AtomicBool::new(false)));
let window = self.inner.write(pa).list.remove(win);
window.add(pa, node.clone(), None, Location::Spawned(id));
self.inner.write(pa).list.insert(win, window);
hook::trigger(pa, WidgetOpened(node.handle().try_downcast::<W>().unwrap()));
callback(pa, node.handle().clone());
node.handle().try_downcast()
}
pub(crate) fn spawn_on_text<W: Widget>(
&self,
pa: &mut Pass,
(id, specs): (SpawnId, DynSpawnSpecs),
widget: W,
win: usize,
master: Handle<dyn Widget>,
is_closed: Arc<AtomicBool>,
) -> Handle<W> {
let widget = RwData::new(widget);
let path = widget
.read_as::<Buffer>(pa)
.and_then(|buffer| buffer.path_set());
let spawned = self
.ui
.new_dyn_spawned(path.as_ref().map(|p| p.as_ref()), id, specs, win);
let node = Node::new(widget, spawned, Some(master), is_closed);
let window = self.inner.write(pa).list.remove(win);
window.add(pa, node.clone(), None, Location::Spawned(id));
self.inner.write(pa).list.insert(win, window);
hook::trigger(pa, WidgetOpened(node.handle().try_downcast::<W>().unwrap()));
node.handle().try_downcast().unwrap()
}
fn spawn_static<W: Widget>(
&self,
pa: &mut Pass,
(specs, win): (StaticSpawnSpecs, usize),
widget: W,
) -> Option<Handle<W>> {
let id = SpawnId::new();
let widget = RwData::new(widget);
let path = widget
.read_as::<Buffer>(pa)
.and_then(|buffer| buffer.path_set());
let spawned = self
.ui
.new_static_spawned(path.as_ref().map(|p| p.as_ref()), id, specs, win);
let node = Node::new(widget, spawned, None, Arc::new(AtomicBool::new(false)));
let window = self.inner.write(pa).list.remove(win);
window.add(pa, node.clone(), None, Location::Spawned(id));
self.inner.write(pa).list.insert(win, window);
hook::trigger(pa, WidgetOpened(node.handle().try_downcast::<W>().unwrap()));
node.handle().try_downcast()
}
pub(crate) fn new_buffer(&self, pa: &mut Pass, buffer: Buffer) -> Node {
let inner = self.inner.read(pa);
let (handle, specs) = inner.layout.lock().unwrap().new_buffer(pa, &inner.list);
let specs = PushSpecs { cluster: false, ..specs };
if let Some(master) = handle.area().get_cluster_master(pa) {
self.push(pa, (&master, Some(true), specs), buffer, None)
.unwrap()
} else {
self.push(pa, (&handle.area, Some(true), specs), buffer, None)
.unwrap()
}
}
fn push<W: Widget>(
&self,
pa: &mut Pass,
(target, on_buffers, mut specs): (&RwArea, Option<bool>, PushSpecs),
widget: W,
master: Option<&RwArea>,
) -> Option<Node> {
let inner = self.inner.read(pa);
let win = inner
.list
.iter()
.position(|window| {
window.0.read(pa).master_area.is_master_of(pa, target)
|| window
.nodes(pa)
.any(|node| node.area().is_master_of(pa, target))
})
.unwrap();
let inner_window = inner.list[win].0.read(pa);
let target_is_on_buffers = inner_window.buffers_area.is_master_of(pa, target);
let on_buffers = on_buffers.unwrap_or(target_is_on_buffers) && target_is_on_buffers;
if target_is_on_buffers && !on_buffers {
specs.cluster = false;
}
let location = if on_buffers {
Location::OnBuffers
} else if let Some((id, _)) = inner_window
.spawned
.iter()
.find(|(_, node)| node.area().is_eq(pa, target))
{
Location::Spawned(*id)
} else {
Location::Regular
};
let widget = RwData::new(widget);
let path = widget
.read_as::<Buffer>(pa)
.and_then(|buffer| buffer.path_set());
let (pushed, parent) =
target.push(pa, path.as_ref().map(|p| p.as_ref()), specs, on_buffers)?;
let master = master.and_then(|area| {
self.entries(pa)
.find_map(|(.., node)| node.area().is_eq(pa, area).then(|| node.handle().clone()))
});
let node = Node::new(widget, pushed, master, Arc::new(AtomicBool::new(false)));
let window = self.inner.write(pa).list.remove(win);
window.add(pa, node.clone(), parent, location);
self.inner.write(pa).list.insert(win, window);
hook::trigger(pa, WidgetOpened(node.handle().try_downcast::<W>().unwrap()));
Some(node)
}
pub(crate) fn close<W: Widget + ?Sized>(
&self,
pa: &mut Pass,
handle: &Handle<W>,
) -> Result<(), Text> {
let win = self.handle_window(pa, handle)?;
if let Some(buf_handle) = handle.try_downcast::<Buffer>() {
hook::trigger(pa, BufferClosed(buf_handle.clone()));
let buffers_ahead: Vec<Node> = self.inner.read(pa).list[win]
.nodes(pa)
.filter(|node| {
node.handle().read_as::<Buffer>(pa).is_some_and(|buffer| {
buffer.layout_order > buf_handle.read(pa).layout_order
})
})
.cloned()
.collect();
for buffer_ahead in buffers_ahead {
self.swap(pa, handle, buffer_ahead.handle())?;
}
}
let mut list = std::mem::take(&mut self.inner.write(pa).list);
if list[win].close(pa, handle) {
list.remove(win);
self.ui.remove_window(win);
let cur_win = context::current_win_index(pa);
if cur_win > win {
self.inner.write(pa).cur_win -= 1;
}
}
let inner = self.inner.write(pa);
inner.list = list;
inner.new_additions.lock().unwrap().get_or_insert_default();
let inner = self.inner.read(pa);
if handle == inner.cur_widget.read(pa).handle() || handle == inner.cur_buffer.read(pa) {
if let Some(handle) = handle.try_downcast::<Buffer>() {
self.inner.write(pa).buffer_history.remove(&handle);
let entry = self
.inner
.write(pa)
.buffer_history
.jump_by(handle.clone(), -1)
.or_else(|| self.buffers(pa).first().cloned())
.and_then(|handle| {
self.entries(pa).find_map(|(win, node)| {
(*node.handle() == handle).then(|| (win, node.clone()))
})
});
if let Some((_, node)) = entry {
crate::mode::reset_to(pa, node.handle());
} else {
context::sender().send(crate::session::DuatEvent::Quit);
return Ok(());
}
} else {
crate::mode::reset_to(pa, &inner.cur_buffer.read(pa).clone());
}
}
Ok(())
}
pub(crate) fn swap<W1: Widget + ?Sized, W2: Widget + ?Sized>(
&self,
pa: &mut Pass,
lhs: &Handle<W1>,
rhs: &Handle<W2>,
) -> Result<(), Text> {
let lhs_win = self.handle_window(pa, lhs)?;
let rhs_win = self.handle_window(pa, rhs)?;
let [lhs_buffer, rhs_buffer] = [lhs.try_downcast::<Buffer>(), rhs.try_downcast()];
if let [Some(lhs), Some(rhs)] = [lhs_buffer, rhs_buffer] {
let lhs_lo = lhs.read(pa).layout_order;
let rhs_lo = std::mem::replace(&mut rhs.write(pa).layout_order, lhs_lo);
lhs.write(pa).layout_order = rhs_lo
}
let windows = std::mem::take(&mut self.inner.write(pa).list);
let lhs_nodes = windows[lhs_win].take_with_related_nodes(pa, lhs);
windows[rhs_win].insert_nodes(pa, lhs_nodes);
let rhs_nodes = windows[rhs_win].take_with_related_nodes(pa, rhs);
windows[lhs_win].insert_nodes(pa, rhs_nodes);
let wins = self.inner.write(pa);
wins.list = windows;
wins.new_additions.lock().unwrap().get_or_insert_default();
lhs.area().swap(pa, rhs.area());
let cur_buffer = context::current_buffer(pa);
if lhs_win != rhs_win {
if *lhs == cur_buffer {
self.inner.write(pa).cur_win = lhs_win;
self.ui.switch_window(lhs_win);
} else if *rhs == cur_buffer {
self.inner.write(pa).cur_win = rhs_win;
self.ui.switch_window(rhs_win);
}
}
Ok(())
}
pub(crate) fn open_or_move_to_new_window(
&self,
pa: &mut Pass,
pk: PathKind,
default_buffer_opts: BufferOpts,
) -> Node {
let node = match self.buffer_entry(pa, pk.clone()) {
Ok((win, handle)) if self.get(pa, win).unwrap().buffers(pa).len() > 1 => {
handle.write(pa).layout_order = 0;
let nodes = {
let old_window = self.inner.write(pa).list.remove(win);
let nodes = old_window.take_with_related_nodes(pa, &handle.to_dyn());
self.inner.write(pa).list.insert(win, old_window);
nodes
};
let path = handle.read(pa).path_set();
let new_root = self.ui.new_root(path.as_ref().map(|p| p.as_ref()));
handle.area().swap(pa, &new_root);
let window = Window::new_from_raw(
pa,
win,
handle.area.clone(),
nodes,
self.inner.read(pa).new_additions.clone(),
);
self.inner.write(pa).list.push(window.clone());
hook::trigger(pa, WindowOpened(window));
let lo = handle.read(pa).layout_order;
for handle in &self.inner.read(pa).list[win].buffers(pa)[lo..] {
new_root.swap(pa, handle.area());
}
new_root.delete(pa);
self.inner
.write(pa)
.new_additions
.lock()
.unwrap()
.get_or_insert_default();
Node::from_handle(handle)
}
Ok((.., handle)) => Node::from_handle(handle),
Err(_) => self.new_window(pa, Buffer::new(pk.as_path(), default_buffer_opts)),
};
if context::current_buffer(pa).read(pa).path_kind() != pk {
mode::reset_to(pa, node.handle());
}
node
}
#[track_caller]
pub(crate) fn set_current_node(&self, pa: &mut Pass, node: Node) -> Result<(), Text> {
let internal_pass = &mut unsafe { Pass::new() };
let win = self.handle_window(pa, node.handle())?;
let inner = self.inner.write(pa);
if let Some(current) = node.try_downcast::<Buffer>() {
let former = std::mem::replace(inner.cur_buffer.write(internal_pass), current.clone());
inner.buffer_history.insert(former.clone(), current.clone());
hook::trigger(pa, BufferSwitched((former, current)));
}
let inner = self.inner.write(pa);
*inner.cur_widget.write(internal_pass) = node.clone();
inner.cur_win = win;
self.ui.switch_window(win);
Ok(())
}
pub(crate) fn queue_close_spawned(&self, id: SpawnId) {
let mut spawns_to_remove = self.spawns_to_remove.lock().unwrap();
if !spawns_to_remove.contains(&id) {
spawns_to_remove.push(id)
}
}
pub(crate) fn cleanup_despawned(&self, pa: &mut Pass) {
let spawns_to_remove = std::mem::take(&mut *self.spawns_to_remove.lock().unwrap());
for id in spawns_to_remove {
if let Some((_, node)) = self
.iter(pa)
.flat_map(|window| &window.0.read(pa).spawned)
.find(|(other, _)| *other == id)
{
self.close(pa, &node.handle().clone()).unwrap();
}
}
}
pub(crate) fn handle_window<W: Widget + ?Sized>(
&self,
pa: &Pass,
handle: &Handle<W>,
) -> Result<usize, Text> {
self.entries(pa)
.find_map(|(win, node)| (node.handle() == handle).then_some(win))
.ok_or_else(|| txt!("The Handle was already closed"))
}
pub(crate) fn buffer_entry(&self, pa: &Pass, pk: PathKind) -> Result<(usize, Handle), Text> {
self.entries(pa)
.find_map(|(win, node)| {
(node.read_as(pa).filter(|f: &&Buffer| f.path_kind() == pk))
.and_then(|_| node.try_downcast().map(|handle| (win, handle)))
})
.ok_or_else(|| txt!("Buffer {pk} not found"))
}
pub(crate) fn named_buffer_entry(&self, pa: &Pass, name: &str) -> Option<(usize, Handle)> {
self.entries(pa).find_map(|(win, node)| {
(node.read_as(pa).filter(|f: &&Buffer| f.name() == name))
.and_then(|_| node.try_downcast().map(|handle| (win, handle)))
})
}
pub(crate) fn path_buffer_entry(&self, pa: &Pass, path: &Path) -> Option<(usize, Handle)> {
self.entries(pa).find_map(|(win, node)| {
(node
.read_as(pa)
.filter(|f: &&Buffer| f.path_kind().as_path().is_some_and(|p| p == path)))
.and_then(|_| node.try_downcast().map(|handle| (win, handle)))
})
}
pub(crate) fn node_of<'a, W: Widget>(&'a self, pa: &'a Pass) -> Result<&'a Node, Text> {
let buffer = context::current_buffer(pa);
if let Some((handle, _)) = buffer.get_related::<W>(pa).first() {
self.entries(pa)
.find_map(|(.., node)| node.ptr_eq(handle.widget()).then_some(node))
} else {
let cur_win = self.inner.read(pa).cur_win;
let list = &self.inner.read(pa).list;
list[cur_win]
.nodes(pa)
.chain(list[cur_win + 1..].iter().flat_map(|win| win.nodes(pa)))
.chain(list[..cur_win].iter().flat_map(|win| win.nodes(pa)))
.find(|node| node.data_is::<W>())
}
.ok_or(txt!(
"No widget of type [a]{}[] found",
std::any::type_name::<W>()
))
}
pub(crate) fn entries<'a>(&'a self, pa: &'a Pass) -> impl Iterator<Item = (usize, &'a Node)> {
self.inner
.read(pa)
.list
.iter()
.enumerate()
.flat_map(|(win, window)| {
let inner = window.0.read(pa);
inner
.nodes
.iter()
.chain(inner.spawned.iter().map(|(_, node)| node))
.map(move |node| (win, node))
})
}
pub(crate) fn iter_around<'a>(
&'a self,
pa: &'a Pass,
win: usize,
wid: usize,
) -> impl Iterator<Item = (usize, &'a Node)> + 'a {
let window_entries =
|(w, window): (usize, &'a Window)| window.nodes(pa).map(move |node| (w, node));
let windows = &self.inner.read(pa).list;
let prev_len: usize = windows
.iter()
.take(win)
.map(|win| win.len_widgets(pa))
.sum();
windows
.iter()
.enumerate()
.skip(win)
.flat_map(window_entries)
.skip(wid + 1)
.chain(
windows
.iter()
.enumerate()
.take(win + 1)
.flat_map(window_entries)
.take(prev_len + wid),
)
}
pub(crate) fn iter_around_rev<'a>(
&'a self,
pa: &'a Pass,
win: usize,
wid: usize,
) -> impl Iterator<Item = (usize, &'a Node)> + 'a {
let entries =
|(w, window): (usize, &'a Window)| window.nodes(pa).map(move |node| (w, node));
let windows = &self.inner.read(pa).list;
let next_len: usize = windows
.iter()
.skip(win)
.map(|win| win.len_widgets(pa))
.sum();
windows
.iter()
.enumerate()
.rev()
.skip(windows.len() - (win + 1))
.flat_map(move |(w, win)| entries((w, win)).rev().skip(win.len_widgets(pa) - wid))
.chain(
windows
.iter()
.enumerate()
.rev()
.take(windows.len() - win)
.flat_map(move |(i, win)| entries((i, win)).rev())
.take(next_len - (wid + 1)),
)
}
pub fn jump_buffers_by(&self, pa: &mut Pass, jumps: i32) {
let current = self.inner.read(pa).cur_buffer.read(pa).clone();
if let Some(handle) = self.inner.write(pa).buffer_history.jump_by(current, jumps) {
mode::reset_to(pa, &handle);
} else {
context::warn!("No buffer [a]{jumps}[] jumps away from the current one");
}
}
pub fn last_switched_buffer(&self, pa: &mut Pass) -> Result<Handle, Text> {
let current = self.inner.read(pa).cur_buffer.read(pa).clone();
if let Some(handle) = self.inner.write(pa).buffer_history.last(current) {
mode::reset_to(pa, &handle);
Ok(handle)
} else {
Err(txt!("No last buffer"))
}
}
pub fn size(&self) -> Coord {
self.ui.size()
}
pub fn len(&self, pa: &Pass) -> usize {
self.inner.read(pa).list.len()
}
pub fn get<'a>(&'a self, pa: &'a Pass, win: usize) -> Option<&'a Window> {
self.inner.read(pa).list.get(win)
}
pub fn iter<'a>(&'a self, pa: &'a Pass) -> std::slice::Iter<'a, Window> {
self.inner.read(pa).list.iter()
}
pub fn handles<'a>(&'a self, pa: &'a Pass) -> impl Iterator<Item = Handle<dyn Widget>> + 'a {
self.inner
.read(pa)
.list
.iter()
.flat_map(|w| w.nodes(pa).map(|n| n.handle().clone()))
}
pub fn handles_of<W: Widget>(&self, pa: &Pass) -> Vec<Handle<W>> {
self.inner
.read(pa)
.list
.iter()
.flat_map(|w| w.nodes(pa).filter_map(|n| n.handle().try_downcast()))
.collect()
}
pub fn buffers(&self, pa: &Pass) -> Vec<Handle> {
self.inner
.read(pa)
.list
.iter()
.flat_map(|w| w.buffers(pa))
.collect()
}
pub fn current_window(&self, pa: &Pass) -> usize {
self.inner.read(pa).cur_win
}
pub(crate) fn current_buffer<'a>(&'a self, pa: &'a Pass) -> &'a RwData<Handle> {
&self.inner.read(pa).cur_buffer
}
pub(crate) fn current_widget<'a>(&'a self, pa: &'a Pass) -> &'a RwData<Node> {
&self.inner.read(pa).cur_widget
}
pub(crate) fn get_additions(&self, pa: &mut Pass) -> Option<Vec<(usize, Node)>> {
self.inner.write(pa).new_additions.lock().unwrap().take()
}
}
#[derive(Clone)]
pub struct Window(RwData<InnerWindow>);
struct InnerWindow {
index: usize,
nodes: Vec<Node>,
spawned: Vec<(SpawnId, Node)>,
buffers_area: RwArea,
master_area: RwArea,
new_additions: Arc<Mutex<Option<Vec<(usize, Node)>>>>,
}
impl Window {
fn new<W: Widget>(
index: usize,
pa: &mut Pass,
ui: Ui,
widget: W,
new_additions: Arc<Mutex<Option<Vec<(usize, Node)>>>>,
) -> (Self, Node) {
let widget = RwData::new(widget);
let path = if let Some(buffer) = widget.write_as::<Buffer>(pa) {
buffer.layout_order = get_layout_order();
buffer.path_set()
} else {
None
};
let area = ui.new_root(path.as_ref().map(|p| p.as_ref()));
let node = Node::new::<W>(widget, area.clone(), None, Arc::new(AtomicBool::new(false)));
new_additions
.lock()
.unwrap()
.get_or_insert_default()
.push((index, node.clone()));
let window = Self(RwData::new(InnerWindow {
index,
nodes: vec![node.clone()],
spawned: Vec::new(),
buffers_area: area.clone(),
master_area: area.clone(),
new_additions,
}));
(window, node)
}
pub(crate) fn new_from_raw(
pa: &mut Pass,
index: usize,
master_area: RwArea,
nodes: Vec<Node>,
new_additions: Arc<Mutex<Option<Vec<(usize, Node)>>>>,
) -> Self {
let master_area = master_area
.get_cluster_master(pa)
.unwrap_or(master_area.clone());
Self(RwData::new(InnerWindow {
index,
nodes,
spawned: Vec::new(),
buffers_area: master_area.clone(),
master_area,
new_additions,
}))
}
pub fn push_inner<W: Widget>(
&self,
pa: &mut Pass,
widget: W,
mut specs: PushSpecs,
) -> Handle<W> {
let target = self.0.read(pa).buffers_area.clone();
specs.cluster = false;
context::windows()
.push_widget(pa, (&target, Some(false), specs), widget, None)
.unwrap()
}
pub fn push_outer<W: Widget>(
&self,
pa: &mut Pass,
widget: W,
mut specs: PushSpecs,
) -> Handle<W> {
let target = self.0.read(pa).master_area.clone();
specs.cluster = false;
context::windows()
.push_widget(pa, (&target, Some(false), specs), widget, None)
.unwrap()
}
pub fn spawn<W: Widget>(&self, pa: &mut Pass, widget: W, specs: StaticSpawnSpecs) -> Handle<W> {
context::windows()
.spawn_static(pa, (specs, self.0.read(pa).index), widget)
.unwrap()
}
fn add(&self, pa: &mut Pass, node: Node, parent: Option<RwArea>, location: Location) {
match location {
Location::OnBuffers => {
self.0.write(pa).nodes.push(node.clone());
if let Some(parent) = &parent
&& parent.is_master_of(pa, &self.0.read(pa).buffers_area)
{
self.0.write(pa).buffers_area = parent.clone()
}
}
Location::Regular => self.0.write(pa).nodes.push(node.clone()),
Location::Spawned(id) => self.0.write(pa).spawned.push((id, node.clone())),
}
if let Some(parent) = &parent
&& parent.is_master_of(pa, &self.0.read(pa).master_area)
{
self.0.write(pa).master_area = parent.clone()
}
let inner = self.0.write(pa);
inner
.new_additions
.lock()
.unwrap()
.get_or_insert_default()
.push((inner.index, node.clone()));
}
fn close<W: Widget + ?Sized>(&self, pa: &mut Pass, handle: &Handle<W>) -> bool {
let handle_eq = |node: &mut Node| node.handle() == handle;
let inner = self.0.write(pa);
let node = if let Some(node) = inner.nodes.extract_if(.., handle_eq).next() {
node
} else if let Some((_, node)) = inner.spawned.extract_if(.., |(_, n)| handle_eq(n)).next() {
node
} else {
unreachable!("This isn't supposed to fail");
};
node.handle().declare_closed();
let (do_rm_window, rm_areas) = node.area().delete(pa);
if do_rm_window {
for handle in self.handles(pa).cloned().collect::<Vec<_>>() {
handle.declare_closed();
}
return true;
}
let (mut nodes, mut spawned) = {
let inner = self.0.write(pa);
let nodes = std::mem::take(&mut inner.nodes);
let spawned = std::mem::take(&mut inner.spawned);
(nodes, spawned)
};
nodes.retain(|node| {
if rm_areas.iter().any(|a| a.is_eq(pa, node.handle().area())) {
node.handle().declare_closed();
false
} else {
true
}
});
spawned.retain(|(_, node)| {
if rm_areas.iter().any(|a| a.is_eq(pa, node.handle().area())) {
node.handle().declare_closed();
false
} else {
true
}
});
let inner = self.0.write(pa);
inner.nodes = nodes;
inner.spawned = spawned;
let buffers = self.buffers(pa);
if buffers.len() == 1 {
let handle = buffers.first().unwrap();
let master_area = handle
.area()
.get_cluster_master(pa)
.unwrap_or(handle.area.clone());
self.0.write(pa).buffers_area = master_area;
}
if self.buffers(pa).is_empty() {
for handle in self.handles(pa).cloned().collect::<Vec<_>>() {
handle.declare_closed();
}
true
} else {
false
}
}
fn take_with_related_nodes<W: Widget + ?Sized>(
&self,
pa: &mut Pass,
handle: &Handle<W>,
) -> Vec<Node> {
let related = handle.related();
let (related, inner) = pa.write_many((related, &self.0));
inner
.nodes
.extract_if(.., |node| {
related.iter().any(|(handle, _)| handle == node.handle()) || node.handle() == handle
})
.collect()
}
fn insert_nodes(&self, pa: &mut Pass, nodes: Vec<Node>) {
self.0.write(pa).nodes.extend(nodes);
}
pub(crate) fn nodes<'a>(&'a self, pa: &'a Pass) -> Nodes<'a> {
let inner = self.0.read(pa);
let spawned = SpawnedNodes { iter: inner.spawned.iter() };
Nodes {
iter: inner.nodes.iter().chain(spawned),
len: inner.nodes.len() + inner.spawned.len(),
taken: 0,
}
}
pub fn handles<'a>(&'a self, pa: &'a Pass) -> impl Iterator<Item = &'a Handle<dyn Widget>> {
self.nodes(pa).map(|node| node.handle())
}
pub fn buffers(&self, pa: &Pass) -> Vec<Handle> {
let inner = self.0.read(pa);
let mut buffers: Vec<Handle> = inner
.nodes
.iter()
.filter_map(|node| node.try_downcast())
.collect();
buffers.sort_unstable_by_key(|buffer| buffer.read(pa).layout_order);
buffers.extend(
inner
.spawned
.iter()
.filter_map(|(_, node)| node.try_downcast()),
);
buffers
}
pub(crate) fn send_mouse_event(&self, pa: &mut Pass, mouse_event: UiMouseEvent) {
let inner = self.0.read(pa);
let node = inner
.spawned
.iter()
.rev()
.map(|(_, node)| node)
.chain(&inner.nodes)
.find(|node| {
let tl = node.handle().area().top_left(pa);
let br = node.handle().area().bottom_right(pa);
(tl.x <= mouse_event.coord.x && tl.y <= mouse_event.coord.y)
&& (mouse_event.coord.x < br.x && mouse_event.coord.y < br.y)
})
.cloned();
if let Some(node) = node {
node.on_mouse_event(pa, mouse_event);
}
}
pub(crate) fn len_widgets(&self, pa: &Pass) -> usize {
let inner = self.0.read(pa);
inner.nodes.len() + inner.spawned.len()
}
pub fn width(&self) -> f32 {
crate::context::windows().ui.size().x
}
pub fn height(&self) -> f32 {
crate::context::windows().ui.size().y
}
}
#[derive(Default)]
struct BufferHistory {
current_i: usize,
list: Vec<Handle>,
last: Option<Handle>,
}
impl BufferHistory {
fn jump_by(&mut self, current: Handle, by: i32) -> Option<Handle> {
let new_i = self
.current_i
.saturating_add_signed(by as isize)
.min(self.list.len());
let new_handle = self.list.get(new_i)?.clone();
self.last = Some(current);
self.current_i = new_i;
Some(new_handle)
}
fn last(&mut self, current: Handle) -> Option<Handle> {
if let Some(last) = self.last.as_mut() {
Some(std::mem::replace(last, current))
} else if let Some(last) = self.list.get(self.current_i.checked_sub(1)?) {
self.last = Some(current);
Some(last.clone())
} else {
None
}
}
fn insert(&mut self, current: Handle, handle: Handle) {
if self
.current_i
.checked_sub(1)
.is_none_or(|prev_i| self.list[prev_i] != handle)
&& self
.list
.get(self.current_i)
.is_none_or(|other| *other != handle)
{
self.list.insert(self.current_i, handle);
self.last = Some(current);
self.current_i += 1;
}
}
fn remove(&mut self, handle: &Handle) {
let mut i = 0;
self.list.retain(|other| {
if other == handle {
if i < self.current_i {
self.current_i -= 1;
}
false
} else {
i += 1;
true
}
});
}
}
fn get_layout_order() -> usize {
use std::sync::atomic::{AtomicUsize, Ordering};
static LAYOUT_ORDER: AtomicUsize = AtomicUsize::new(0);
LAYOUT_ORDER.fetch_add(1, Ordering::Relaxed)
}
enum Location {
OnBuffers,
Regular,
Spawned(SpawnId),
}
#[derive(Debug, Clone)]
pub(crate) struct Nodes<'a> {
iter: Chain<std::slice::Iter<'a, Node>, SpawnedNodes<'a>>,
len: usize,
taken: usize,
}
impl<'a> Iterator for Nodes<'a> {
type Item = &'a Node;
fn next(&mut self) -> Option<Self::Item> {
let next = self.iter.next();
self.taken += next.is_some() as usize;
next
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len - self.taken, Some(self.len - self.taken))
}
}
impl<'a> DoubleEndedIterator for Nodes<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
let next = self.iter.next_back();
self.taken += next.is_some() as usize;
next
}
}
impl ExactSizeIterator for Nodes<'_> {}
#[derive(Debug, Clone)]
struct SpawnedNodes<'a> {
iter: std::slice::Iter<'a, (SpawnId, Node)>,
}
impl<'a> Iterator for SpawnedNodes<'a> {
type Item = &'a Node;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(_, node)| node)
}
}
impl DoubleEndedIterator for SpawnedNodes<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back().map(|(_, node)| node)
}
}