use std::sync::{Arc, Weak};
use crate::{
event::{Event, EventArgs},
update::UPDATES,
var::*,
widget::{WidgetHandlesCtx, WidgetId, WidgetUpdateMode, node::IntoUiNode},
};
type SlotId = usize;
struct SlotData {
item: Mutex<UiNode>,
slots: Mutex<SlotsData>,
}
#[derive(Default)]
struct SlotsData {
next_slot: SlotId,
owner: Option<(SlotId, WidgetId)>,
move_request: Option<(SlotId, WidgetId)>,
replacement: Option<UiNode>,
}
impl SlotsData {
fn next_slot(&mut self) -> SlotId {
let r = self.next_slot;
self.next_slot = self.next_slot.wrapping_add(1);
r
}
}
pub struct ArcNode(Arc<SlotData>);
impl Clone for ArcNode {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl ArcNode {
pub fn new(node: impl IntoUiNode) -> Self {
Self::new_impl(node.into_node())
}
fn new_impl(node: UiNode) -> Self {
ArcNode(Arc::new(SlotData {
item: Mutex::new(node),
slots: Mutex::default(),
}))
}
pub fn new_cyclic(node: impl FnOnce(WeakNode) -> UiNode) -> 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 {
WeakNode(Arc::downgrade(&self.0))
}
pub fn set(&self, new_node: impl IntoUiNode) {
self.set_impl(new_node.into_node())
}
fn set_impl(&self, new_node: UiNode) {
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>) -> UiNode {
self.take_when_impl(var.into_var())
}
fn take_when_impl(&self, var: Var<bool>) -> UiNode {
impls::TakeSlot {
slot: self.0.slots.lock().next_slot(),
rc: self.0.clone(),
take: impls::TakeWhenVar { var: var.into_var() },
wgt_handles: WidgetHandlesCtx::new(),
}
.into_node()
}
pub fn take_on<A, F>(&self, event: Event<A>, filter: F, take_on_init: bool) -> UiNode
where
A: EventArgs,
F: FnMut(&A) -> bool + Send + 'static,
{
impls::TakeSlot {
slot: self.0.slots.lock().next_slot(),
rc: self.0.clone(),
take: impls::TakeOnEvent {
event,
filter,
take_on_init,
},
wgt_handles: WidgetHandlesCtx::new(),
}
.into_node()
}
pub fn take_on_init(&self) -> UiNode {
self.take_when(true)
}
pub fn try_node<R>(&self, visitor: impl FnOnce(&mut UiNode) -> R) -> Option<R> {
Some(visitor(&mut *self.0.item.try_lock()?))
}
pub fn try_context<R>(&self, update_mode: WidgetUpdateMode, visitor: impl FnOnce() -> R) -> Option<R> {
Some(self.0.item.try_lock()?.as_widget()?.with_context(update_mode, visitor))
}
}
pub struct WeakNode(Weak<SlotData>);
impl Clone for WeakNode {
fn clone(&self) -> Self {
Self(Weak::clone(&self.0))
}
}
impl WeakNode {
pub fn upgrade(&self) -> Option<ArcNode> {
self.0.upgrade().map(ArcNode)
}
}
use parking_lot::Mutex;
use super::UiNode;
mod impls {
use std::sync::Arc;
use zng_layout::unit::PxSize;
use zng_var::Var;
use crate::{
event::{Event, EventArgs},
render::{FrameBuilder, FrameUpdate},
update::{UPDATES, WidgetUpdates},
widget::{
WIDGET, WidgetHandlesCtx,
info::{WidgetInfoBuilder, WidgetLayout, WidgetMeasure},
node::{UiNode, UiNodeImpl, WidgetUiNodeImpl},
},
};
use super::{SlotData, SlotId};
pub(super) trait TakeOn: Send + 'static {
fn take_on_init(&mut self) -> bool {
false
}
fn take_on_update(&mut self, updates: &WidgetUpdates) -> bool {
let _ = updates;
false
}
}
pub(super) struct TakeWhenVar {
pub(super) var: Var<bool>,
}
impl TakeOn for TakeWhenVar {
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_update(&mut self, _: &WidgetUpdates) -> bool {
let mut any = false;
self.event.each_update(true, |a| any |= (self.filter)(a));
any
}
}
pub(super) struct TakeSlot<T: TakeOn> {
pub(super) slot: SlotId,
pub(super) rc: Arc<SlotData>,
pub(super) take: T,
pub(super) wgt_handles: WidgetHandlesCtx,
}
impl<T: TakeOn> TakeSlot<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
&& *slot == self.slot
{
slots.owner = None;
was_owner = true;
}
}
if was_owner {
WIDGET.with_handles(&mut self.wgt_handles, || self.rc.item.lock().deinit());
}
self.wgt_handles.clear();
}
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();
node.deinit();
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, || {
node.deinit();
});
self.wgt_handles.clear();
WIDGET.with_handles(&mut self.wgt_handles, || {
new.init();
});
*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
&& *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.rc.item.lock().init();
});
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(&UiNode) -> 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 UiNode) -> 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 UiNode) -> R) -> Option<R> {
if self.is_owner() {
WIDGET.with_handles(&mut self.wgt_handles, || Some(del(&mut self.rc.item.lock())))
} else {
None
}
}
}
impl<T: TakeOn> UiNodeImpl for TakeSlot<T> {
fn children_len(&self) -> usize {
self.delegate_owned(|n| n.0.children_len()).unwrap_or(0)
}
fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
self.delegate_owned_mut(|n| n.0.with_child(index, visitor));
}
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.0.info(info));
}
fn update(&mut self, updates: &WidgetUpdates) {
self.delegate_owned_mut_with_handles(|n| n.0.update(updates));
self.on_update(updates);
}
fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn crate::widget::node::UiNodeListObserver) {
self.delegate_owned_mut(|n| n.0.update_list(updates, observer));
self.on_update(updates);
}
fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
self.delegate_owned_mut(|n| n.0.measure(wm)).unwrap_or_default()
}
fn measure_list(
&mut self,
wm: &mut WidgetMeasure,
measure: &(dyn Fn(usize, &mut UiNode, &mut WidgetMeasure) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.delegate_owned_mut(|n| n.0.measure_list(wm, measure, fold_size))
.unwrap_or_default()
}
fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
self.delegate_owned_mut(|n| n.0.layout(wl)).unwrap_or_default()
}
fn layout_list(
&mut self,
wl: &mut WidgetLayout,
layout: &(dyn Fn(usize, &mut UiNode, &mut WidgetLayout) -> PxSize + Sync),
fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
) -> PxSize {
self.delegate_owned_mut(|n| n.0.layout_list(wl, layout, fold_size))
.unwrap_or_default()
}
fn render(&mut self, frame: &mut FrameBuilder) {
self.delegate_owned_mut(|n| n.0.render(frame));
}
fn render_list(&mut self, frame: &mut FrameBuilder, render: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
self.delegate_owned_mut(|n| n.0.render_list(frame, render));
}
fn render_update(&mut self, update: &mut FrameUpdate) {
self.delegate_owned_mut(|n| n.0.render_update(update));
}
fn render_update_list(&mut self, update: &mut FrameUpdate, render_update: &(dyn Fn(usize, &mut UiNode, &mut FrameUpdate) + Sync)) {
self.delegate_owned_mut(|n| n.0.render_update_list(update, render_update));
}
fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
self.delegate_owned_mut(|n| n.0.for_each_child(visitor));
}
fn try_for_each_child(
&mut self,
visitor: &mut dyn FnMut(usize, &mut UiNode) -> std::ops::ControlFlow<zng_var::BoxAnyVarValue>,
) -> std::ops::ControlFlow<zng_var::BoxAnyVarValue> {
self.delegate_owned_mut(|n| n.0.try_for_each_child(visitor))
.unwrap_or(std::ops::ControlFlow::Continue(()))
}
fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
self.delegate_owned_mut(|n| n.0.par_each_child(visitor));
}
fn par_fold_reduce(
&mut self,
identity: zng_var::BoxAnyVarValue,
fold: &(dyn Fn(zng_var::BoxAnyVarValue, usize, &mut UiNode) -> zng_var::BoxAnyVarValue + Sync),
reduce: &(dyn Fn(zng_var::BoxAnyVarValue, zng_var::BoxAnyVarValue) -> zng_var::BoxAnyVarValue + Sync),
) -> zng_var::BoxAnyVarValue {
self.delegate_owned_mut(|n| n.0.par_fold_reduce(identity.clone(), fold, reduce))
.unwrap_or(identity)
}
fn is_list(&self) -> bool {
self.rc.item.lock().0.is_list()
}
fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
if self.delegate_owned_mut(|w| w.as_widget().is_some()).unwrap_or(false) {
Some(self)
} else {
None
}
}
}
impl<T: TakeOn> WidgetUiNodeImpl for TakeSlot<T> {
fn with_context(&mut self, update_mode: crate::widget::WidgetUpdateMode, visitor: &mut dyn FnMut()) {
#[cfg(debug_assertions)]
let mut called = 0;
self.delegate_owned_mut_with_handles(|w| {
#[cfg(debug_assertions)]
{
called = 1;
}
if let Some(mut w) = w.as_widget() {
#[cfg(debug_assertions)]
{
called = 2;
}
w.with_context(update_mode, visitor)
}
});
#[cfg(debug_assertions)]
match called {
0 => tracing::error!("ArcNode TakeSlot node taken while as_widget is held"),
1 => tracing::error!("ArcNode TakeSlot node was widget when as_widget returned, but not anymore"),
_ => {}
}
}
}
}