use std::sync::{Arc, Weak};
use crate::{
event::{Event, EventArgs},
update::UPDATES,
var::*,
widget::{WidgetHandlesCtx, WidgetId, WidgetUpdateMode},
};
type SlotId = usize;
struct SlotData<U> {
item: Mutex<U>,
slots: Mutex<SlotsData<U>>,
}
struct SlotsData<U> {
next_slot: SlotId,
owner: Option<(SlotId, WidgetId)>,
move_request: Option<(SlotId, WidgetId)>,
replacement: Option<U>,
}
impl<U> SlotsData<U> {
fn next_slot(&mut self) -> SlotId {
let r = self.next_slot;
self.next_slot = self.next_slot.wrapping_add(1);
r
}
}
impl<U> Default for SlotsData<U> {
fn default() -> Self {
Self {
next_slot: Default::default(),
owner: Default::default(),
move_request: Default::default(),
replacement: Default::default(),
}
}
}
pub struct ArcNode<U: UiNode>(Arc<SlotData<U>>);
impl<U: UiNode> Clone for ArcNode<U> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<U: UiNode> ArcNode<U> {
pub fn new(node: U) -> Self {
ArcNode(Arc::new(SlotData {
item: Mutex::new(node),
slots: Mutex::default(),
}))
}
pub fn new_cyclic(node: impl FnOnce(WeakNode<U>) -> U) -> Self {
Self(Arc::new_cyclic(|wk| {
let node = node(WeakNode(wk.clone()));
SlotData {
item: Mutex::new(node),
slots: Mutex::default(),
}
}))
}
pub fn downgrade(&self) -> WeakNode<U> {
WeakNode(Arc::downgrade(&self.0))
}
pub fn set(&self, new_node: U) {
let mut slots = self.0.slots.lock();
let slots = &mut *slots;
if let Some((_, id)) = &slots.owner {
slots.replacement = Some(new_node);
let _ = UPDATES.update(*id);
} else {
*self.0.item.lock() = new_node;
}
}
pub fn take_when(&self, var: impl IntoVar<bool>) -> TakeSlot<U, impl TakeOn> {
impls::TakeSlot {
slot: self.0.slots.lock().next_slot(),
rc: self.0.clone(),
take: impls::TakeWhenVar { var: var.into_var() },
delegate_init: |n| n.init(),
delegate_deinit: |n| n.deinit(),
wgt_handles: WidgetHandlesCtx::new(),
}
}
pub fn take_on<A: EventArgs>(
&self,
event: Event<A>,
filter: impl FnMut(&A) -> bool + Send + 'static,
take_on_init: bool,
) -> TakeSlot<U, impl TakeOn> {
impls::TakeSlot {
slot: self.0.slots.lock().next_slot(),
rc: self.0.clone(),
take: impls::TakeOnEvent {
event,
filter,
take_on_init,
},
delegate_init: |n| n.init(),
delegate_deinit: |n| n.deinit(),
wgt_handles: WidgetHandlesCtx::new(),
}
}
pub fn take_on_init(&self) -> TakeSlot<U, impl TakeOn> {
self.take_when(true)
}
pub fn try_context<R>(&self, update_mode: WidgetUpdateMode, f: impl FnOnce() -> R) -> Option<R> {
self.0.item.try_lock()?.with_context(update_mode, f)
}
}
pub struct WeakNode<U: UiNode>(Weak<SlotData<U>>);
impl<U: UiNode> Clone for WeakNode<U> {
fn clone(&self) -> Self {
Self(Weak::clone(&self.0))
}
}
impl<U: UiNode> WeakNode<U> {
pub fn upgrade(&self) -> Option<ArcNode<U>> {
self.0.upgrade().map(ArcNode)
}
}
pub struct ArcNodeList<L: UiNodeList>(Arc<SlotData<L>>);
impl<L: UiNodeList> Clone for ArcNodeList<L> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<L: UiNodeList> ArcNodeList<L> {
pub fn new(list: L) -> Self {
ArcNodeList(Arc::new(SlotData {
item: Mutex::new(list),
slots: Mutex::default(),
}))
}
pub fn new_cyclic(list: impl FnOnce(WeakNodeList<L>) -> L) -> Self {
Self(Arc::new_cyclic(|wk| {
let list = list(WeakNodeList(wk.clone()));
SlotData {
item: Mutex::new(list),
slots: Mutex::default(),
}
}))
}
pub fn downgrade(&self) -> WeakNodeList<L> {
WeakNodeList(Arc::downgrade(&self.0))
}
pub fn set(&self, new_list: L) {
let mut slots = self.0.slots.lock();
let slots = &mut *slots;
if let Some((_, id)) = &slots.owner {
slots.replacement = Some(new_list);
UPDATES.update(*id);
} else {
*self.0.item.lock() = new_list;
}
}
pub fn take_when(&self, var: impl IntoVar<bool>) -> TakeSlot<L, impl TakeOn> {
impls::TakeSlot {
slot: self.0.slots.lock().next_slot(),
rc: self.0.clone(),
take: impls::TakeWhenVar { var: var.into_var() },
delegate_init: |n| n.init_all(),
delegate_deinit: |n| n.deinit_all(),
wgt_handles: WidgetHandlesCtx::new(),
}
}
pub fn take_on<A: EventArgs>(
&self,
event: Event<A>,
filter: impl FnMut(&A) -> bool + Send + 'static,
take_on_init: bool,
) -> TakeSlot<L, impl TakeOn> {
impls::TakeSlot {
slot: self.0.slots.lock().next_slot(),
rc: self.0.clone(),
take: impls::TakeOnEvent {
event,
filter,
take_on_init,
},
delegate_init: |n| n.init_all(),
delegate_deinit: |n| n.deinit_all(),
wgt_handles: WidgetHandlesCtx::new(),
}
}
pub fn take_on_init(&self) -> TakeSlot<L, impl TakeOn> {
self.take_when(true)
}
pub fn for_each_ctx(&self, update_mode: WidgetUpdateMode, mut f: impl FnMut(usize)) {
self.0.item.lock().for_each(|i, n| {
n.with_context(update_mode, || f(i));
})
}
}
pub struct WeakNodeList<L: UiNodeList>(Weak<SlotData<L>>);
impl<L: UiNodeList> Clone for WeakNodeList<L> {
fn clone(&self) -> Self {
Self(Weak::clone(&self.0))
}
}
impl<L: UiNodeList> WeakNodeList<L> {
pub fn upgrade(&self) -> Option<ArcNodeList<L>> {
self.0.upgrade().map(ArcNodeList)
}
}
pub use impls::*;
use parking_lot::Mutex;
use super::{UiNode, UiNodeList};
mod impls {
use std::sync::Arc;
use zng_layout::unit::PxSize;
use zng_var::Var;
use crate::{
event::{Event, EventArgs},
render::{FrameBuilder, FrameUpdate},
update::{EventUpdate, WidgetUpdates, UPDATES},
widget::{
info::{WidgetInfoBuilder, WidgetLayout, WidgetMeasure},
node::{BoxedUiNode, BoxedUiNodeList, UiNode, UiNodeList, UiNodeListObserver},
WidgetHandlesCtx, WidgetUpdateMode, WIDGET,
},
};
use super::{SlotData, SlotId};
#[doc(hidden)]
pub trait TakeOn: Send + 'static {
fn take_on_init(&mut self) -> bool {
false
}
fn take_on_event(&mut self, update: &EventUpdate) -> bool {
let _ = update;
false
}
fn take_on_update(&mut self, updates: &WidgetUpdates) -> bool {
let _ = updates;
false
}
}
pub(super) struct TakeWhenVar<V: Var<bool>> {
pub(super) var: V,
}
impl<V: Var<bool>> TakeOn for TakeWhenVar<V> {
fn take_on_init(&mut self) -> bool {
WIDGET.sub_var(&self.var);
self.var.get()
}
fn take_on_update(&mut self, _: &WidgetUpdates) -> bool {
self.var.get_new().unwrap_or(false)
}
}
pub(super) struct TakeOnEvent<A: EventArgs, F: FnMut(&A) -> bool + Send + 'static> {
pub(super) event: Event<A>,
pub(super) filter: F,
pub(super) take_on_init: bool,
}
impl<A: EventArgs, F: FnMut(&A) -> bool + Send + Send + 'static> TakeOn for TakeOnEvent<A, F> {
fn take_on_init(&mut self) -> bool {
WIDGET.sub_event(&self.event);
self.take_on_init
}
fn take_on_event(&mut self, update: &EventUpdate) -> bool {
if let Some(args) = self.event.on(update) {
(self.filter)(args)
} else {
false
}
}
}
#[doc(hidden)]
pub struct TakeSlot<U, T: TakeOn> {
pub(super) slot: SlotId,
pub(super) rc: Arc<SlotData<U>>,
pub(super) take: T,
pub(super) delegate_init: fn(&mut U),
pub(super) delegate_deinit: fn(&mut U),
pub(super) wgt_handles: WidgetHandlesCtx,
}
impl<U, T: TakeOn> TakeSlot<U, T> {
fn on_init(&mut self) {
if self.take.take_on_init() {
self.take();
}
}
fn on_deinit(&mut self) {
let mut was_owner = false;
{
let mut slots = self.rc.slots.lock();
let slots = &mut *slots;
if let Some((slot, _)) = &slots.owner {
if *slot == self.slot {
slots.owner = None;
was_owner = true;
}
}
}
if was_owner {
WIDGET.with_handles(&mut self.wgt_handles, || (self.delegate_deinit)(&mut *self.rc.item.lock()));
}
self.wgt_handles.clear();
}
fn on_event(&mut self, update: &EventUpdate) {
if !self.is_owner() && self.take.take_on_event(update) {
self.take();
}
}
fn on_update(&mut self, updates: &WidgetUpdates) {
if self.is_owner() {
let mut slots = self.rc.slots.lock();
if let Some((_, id)) = slots.move_request {
let replacement = slots.replacement.take();
slots.owner = None;
drop(slots);
let mut node = self.rc.item.lock();
(self.delegate_deinit)(&mut node);
WIDGET.update_info().layout().render();
if let Some(new) = replacement {
*node = new;
}
UPDATES.update(id);
} else if let Some(mut new) = slots.replacement.take() {
drop(slots);
let mut node = self.rc.item.lock();
WIDGET.with_handles(&mut self.wgt_handles, || {
(self.delegate_deinit)(&mut node);
});
self.wgt_handles.clear();
WIDGET.with_handles(&mut self.wgt_handles, || {
(self.delegate_init)(&mut new);
});
*node = new;
WIDGET.update_info().layout().render();
}
} else if self.take.take_on_update(updates) {
self.take();
} else {
let mut slots = self.rc.slots.lock();
if let Some((slot, _)) = &slots.move_request {
if *slot == self.slot && slots.owner.is_none() {
slots.move_request = None;
drop(slots);
self.take();
}
}
}
}
fn take(&mut self) {
{
let mut slots = self.rc.slots.lock();
let slots = &mut *slots;
if let Some((sl, id)) = &slots.owner {
if *sl != self.slot {
slots.move_request = Some((self.slot, WIDGET.id()));
UPDATES.update(*id);
}
} else {
slots.owner = Some((self.slot, WIDGET.id()));
}
}
if self.is_owner() {
WIDGET.with_handles(&mut self.wgt_handles, || {
(self.delegate_init)(&mut *self.rc.item.lock());
});
WIDGET.update_info().layout().render();
}
}
fn is_owner(&self) -> bool {
self.rc.slots.lock().owner.as_ref().map(|(sl, _)| *sl == self.slot).unwrap_or(false)
}
fn delegate_owned<R>(&self, del: impl FnOnce(&U) -> R) -> Option<R> {
if self.is_owner() {
Some(del(&*self.rc.item.lock()))
} else {
None
}
}
fn delegate_owned_mut<R>(&mut self, del: impl FnOnce(&mut U) -> R) -> Option<R> {
if self.is_owner() {
Some(del(&mut *self.rc.item.lock()))
} else {
None
}
}
fn delegate_owned_mut_with_handles<R>(&mut self, del: impl FnOnce(&mut U) -> R) -> Option<R> {
if self.is_owner() {
WIDGET.with_handles(&mut self.wgt_handles, || Some(del(&mut *self.rc.item.lock())))
} else {
None
}
}
}
impl<U: UiNode, T: TakeOn> UiNode for TakeSlot<U, T> {
fn init(&mut self) {
self.on_init();
}
fn deinit(&mut self) {
self.on_deinit();
}
fn info(&mut self, info: &mut WidgetInfoBuilder) {
self.delegate_owned_mut(|n| n.info(info));
}
fn event(&mut self, update: &EventUpdate) {
self.delegate_owned_mut_with_handles(|n| n.event(update));
self.on_event(update);
}
fn update(&mut self, updates: &WidgetUpdates) {
self.delegate_owned_mut_with_handles(|n| n.update(updates));
self.on_update(updates);
}
fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
self.delegate_owned_mut(|n| n.measure(wm)).unwrap_or_default()
}
fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
self.delegate_owned_mut(|n| n.layout(wl)).unwrap_or_default()
}
fn render(&mut self, frame: &mut FrameBuilder) {
self.delegate_owned_mut(|n| n.render(frame));
}
fn render_update(&mut self, update: &mut FrameUpdate) {
self.delegate_owned_mut(|n| n.render_update(update));
}
fn is_widget(&self) -> bool {
self.delegate_owned(UiNode::is_widget).unwrap_or(false)
}
fn with_context<R, F>(&mut self, update_mode: WidgetUpdateMode, f: F) -> Option<R>
where
F: FnOnce() -> R,
{
self.delegate_owned_mut(|n| n.with_context(update_mode, f)).flatten()
}
}
impl<U: UiNodeList, T: TakeOn> UiNodeList for TakeSlot<U, T> {
fn with_node<R, F>(&mut self, index: usize, f: F) -> R
where
F: FnOnce(&mut BoxedUiNode) -> R,
{
self.delegate_owned_mut(move |l| l.with_node(index, f))
.unwrap_or_else(|| panic!("index `{index}` is >= len `0`"))
}
fn for_each<F>(&mut self, f: F)
where
F: FnMut(usize, &mut BoxedUiNode),
{
self.delegate_owned_mut(|l| l.for_each(f));
}
fn par_each<F>(&mut self, f: F)
where
F: Fn(usize, &mut BoxedUiNode) + Send + Sync,
{
self.delegate_owned_mut(|l| l.par_each(f));
}
fn par_fold_reduce<TF, I, F, R>(&mut self, identity: I, fold: F, reduce: R) -> TF
where
TF: Send + 'static,
I: Fn() -> TF + Send + Sync,
F: Fn(TF, usize, &mut BoxedUiNode) -> TF + Send + Sync,
R: Fn(TF, TF) -> TF + Send + Sync,
{
self.delegate_owned_mut(|l| l.par_fold_reduce(&identity, fold, reduce))
.unwrap_or_else(identity)
}
fn len(&self) -> usize {
self.delegate_owned(UiNodeList::len).unwrap_or(0)
}
fn boxed(self) -> BoxedUiNodeList {
Box::new(self)
}
fn drain_into(&mut self, vec: &mut Vec<BoxedUiNode>) {
self.delegate_owned_mut(|l| l.drain_into(vec));
}
fn init_all(&mut self) {
self.on_init();
}
fn deinit_all(&mut self) {
self.on_deinit();
}
fn info_all(&mut self, info: &mut WidgetInfoBuilder) {
self.delegate_owned_mut_with_handles(|l| l.info_all(info));
}
fn event_all(&mut self, update: &EventUpdate) {
self.delegate_owned_mut_with_handles(|l| l.event_all(update));
self.on_event(update);
}
fn update_all(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
let _ = observer;
self.delegate_owned_mut_with_handles(|l| l.update_all(updates, observer));
self.on_update(updates);
}
fn render_all(&mut self, frame: &mut FrameBuilder) {
self.delegate_owned_mut(|l| l.render_all(frame));
}
fn render_update_all(&mut self, update: &mut FrameUpdate) {
self.delegate_owned_mut(|l| l.render_update_all(update));
}
}
}