use std::sync::Arc;
use task::parking_lot::Mutex;
use zng_app::widget::info;
use crate::prelude::*;
context_var! {
static IS_ENABLED_VAR: bool = true;
}
#[property(CONTEXT, default(true))]
pub fn enabled(child: impl UiNode, enabled: impl IntoVar<bool>) -> impl UiNode {
let enabled = enabled.into_var();
let child = match_node(
child,
clmv!(enabled, |_, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_info(&enabled);
}
UiNodeOp::Info { info } => {
if !enabled.get() {
info.push_interactivity(Interactivity::DISABLED);
}
}
_ => {}
}),
);
with_context_var(child, IS_ENABLED_VAR, merge_var!(IS_ENABLED_VAR, enabled, |&a, &b| a && b))
}
#[property(CONTEXT, default(true))]
pub fn interactive(child: impl UiNode, interactive: impl IntoVar<bool>) -> impl UiNode {
let interactive = interactive.into_var();
match_node(child, move |_, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_info(&interactive);
}
UiNodeOp::Info { info } => {
if !interactive.get() {
info.push_interactivity(Interactivity::BLOCKED);
}
}
_ => {}
})
}
#[property(EVENT)]
pub fn is_enabled(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
event_state(child, state, true, info::INTERACTIVITY_CHANGED_EVENT, move |args| {
if let Some((_, new)) = args.vis_enabled_change(WIDGET.id()) {
Some(new.is_vis_enabled())
} else {
None
}
})
}
#[property(EVENT)]
pub fn is_disabled(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
event_state(child, state, false, info::INTERACTIVITY_CHANGED_EVENT, move |args| {
if let Some((_, new)) = args.vis_enabled_change(WIDGET.id()) {
Some(!new.is_vis_enabled())
} else {
None
}
})
}
event_property! {
pub fn interactivity_changed {
event: info::INTERACTIVITY_CHANGED_EVENT,
args: info::InteractivityChangedArgs,
}
pub fn enabled_changed {
event: info::INTERACTIVITY_CHANGED_EVENT,
args: info::InteractivityChangedArgs,
filter: |a| a.enabled_change(WIDGET.id()).is_some(),
}
pub fn vis_enabled_changed {
event: info::INTERACTIVITY_CHANGED_EVENT,
args: info::InteractivityChangedArgs,
filter: |a| a.vis_enabled_change(WIDGET.id()).is_some(),
}
pub fn blocked_changed {
event: info::INTERACTIVITY_CHANGED_EVENT,
args: info::InteractivityChangedArgs,
filter: |a| a.blocked_change(WIDGET.id()).is_some(),
}
pub fn enable {
event: info::INTERACTIVITY_CHANGED_EVENT,
args: info::InteractivityChangedArgs,
filter: |a| a.is_enable(WIDGET.id()),
}
pub fn disable {
event: info::INTERACTIVITY_CHANGED_EVENT,
args: info::InteractivityChangedArgs,
filter: |a| a.is_disable(WIDGET.id()),
}
pub fn vis_enable {
event: info::INTERACTIVITY_CHANGED_EVENT,
args: info::InteractivityChangedArgs,
filter: |a| a.is_vis_enable(WIDGET.id()),
}
pub fn vis_disable {
event: info::INTERACTIVITY_CHANGED_EVENT,
args: info::InteractivityChangedArgs,
filter: |a| a.is_vis_disable(WIDGET.id()),
}
pub fn block {
event: info::INTERACTIVITY_CHANGED_EVENT,
args: info::InteractivityChangedArgs,
filter: |a| a.is_block(WIDGET.id()),
}
pub fn unblock {
event: info::INTERACTIVITY_CHANGED_EVENT,
args: info::InteractivityChangedArgs,
filter: |a| a.is_unblock(WIDGET.id()),
}
}
#[property(CONTEXT, default(false))]
pub fn modal(child: impl UiNode, enabled: impl IntoVar<bool>) -> impl UiNode {
static MODAL_WIDGETS: StaticStateId<Arc<Mutex<ModalWidgetsData>>> = StaticStateId::new_unique();
#[derive(Default)]
struct ModalWidgetsData {
widgets: IdSet<WidgetId>,
registrar: Option<WidgetId>,
last_in_tree: Option<WidgetInfo>,
}
let enabled = enabled.into_var();
match_node(child, move |_, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_info(&enabled);
WINDOW.init_state_default(&MODAL_WIDGETS); }
UiNodeOp::Deinit => {
let mws = WINDOW.req_state(&MODAL_WIDGETS);
let mut mws = mws.lock();
let widget_id = WIDGET.id();
if mws.widgets.remove(&widget_id) {
if mws.registrar == Some(widget_id) {
mws.registrar = mws.widgets.iter().next().copied();
if let Some(id) = mws.registrar {
UPDATES.update_info(id);
}
}
if mws.last_in_tree.as_ref().map(WidgetInfo::id) == Some(widget_id) {
mws.last_in_tree = None;
}
}
}
UiNodeOp::Info { info } => {
let mws = WINDOW.req_state(&MODAL_WIDGETS);
if enabled.get() {
if let Some(mut a) = info.access() {
a.flag_modal();
}
let insert_filter = {
let mut mws = mws.lock();
let widget_id = WIDGET.id();
if mws.widgets.insert(widget_id) {
mws.last_in_tree = None;
let r = mws.registrar.is_none();
if r {
mws.registrar = Some(widget_id);
}
r
} else {
mws.registrar == Some(widget_id)
}
};
if insert_filter {
info.push_interactivity_filter(clmv!(mws, |a| {
let mut mws = mws.lock();
if mws.last_in_tree.is_none() {
match mws.widgets.len() {
0 => unreachable!(),
1 => {
mws.last_in_tree = a.info.tree().get(*mws.widgets.iter().next().unwrap());
assert!(mws.last_in_tree.is_some());
}
_ => {
let mut found = 0;
for info in a.info.root().self_and_descendants() {
if mws.widgets.contains(&info.id()) {
mws.last_in_tree = Some(info);
found += 1;
if found == mws.widgets.len() {
break;
}
}
}
}
};
}
let modal = mws.last_in_tree.as_ref().unwrap();
if a.info
.self_and_ancestors()
.any(|w| modal.modal_includes(w.id()) || w.modal_included(modal.id()))
{
return Interactivity::ENABLED;
}
if a.info
.self_and_descendants()
.any(|w| modal.modal_includes(w.id()) || w.modal_included(modal.id()))
{
return Interactivity::ENABLED;
}
Interactivity::BLOCKED
}));
}
} else {
let mut mws = mws.lock();
let widget_id = WIDGET.id();
if mws.widgets.remove(&widget_id) && mws.last_in_tree.as_ref().map(|w| w.id()) == Some(widget_id) {
mws.last_in_tree = None;
}
}
}
_ => {}
})
}
#[property(CONTEXT, default(IdSet::new()))]
pub fn modal_includes(child: impl UiNode, includes: impl IntoVar<IdSet<WidgetId>>) -> impl UiNode {
let includes = includes.into_var();
match_node(child, move |_, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_info(&includes);
}
UiNodeOp::Info { info } => includes.with(|w| {
for id in w {
info.insert_modal_include(*id);
}
}),
_ => (),
})
}
#[property(CONTEXT)]
pub fn modal_included(child: impl UiNode, modal_or_descendant: impl IntoVar<WidgetId>) -> impl UiNode {
let modal = modal_or_descendant.into_var();
match_node(child, move |_, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_info(&modal);
}
UiNodeOp::Info { info } => {
info.set_modal_included(modal.get());
}
_ => {}
})
}
pub trait WidgetInfoBuilderModalExt {
fn insert_modal_include(&mut self, include: WidgetId);
fn set_modal_included(&mut self, modal: WidgetId);
}
impl WidgetInfoBuilderModalExt for WidgetInfoBuilder {
fn insert_modal_include(&mut self, include: WidgetId) {
self.with_meta(|mut m| m.entry(&MODAL_INCLUDES).or_default().insert(include));
}
fn set_modal_included(&mut self, modal: WidgetId) {
self.set_meta(&MODAL_INCLUDED, modal);
}
}
trait WidgetInfoModalExt {
fn modal_includes(&self, id: WidgetId) -> bool;
fn modal_included(&self, modal: WidgetId) -> bool;
}
impl WidgetInfoModalExt for WidgetInfo {
fn modal_includes(&self, id: WidgetId) -> bool {
self.id() == id || self.meta().get(&MODAL_INCLUDES).map(|i| i.contains(&id)).unwrap_or(false)
}
fn modal_included(&self, modal: WidgetId) -> bool {
if let Some(id) = self.meta().get_clone(&MODAL_INCLUDED) {
if id == modal {
return true;
}
if let Some(id) = self.tree().get(id) {
return id.ancestors().any(|w| w.id() == modal);
}
}
false
}
}
static MODAL_INCLUDES: StaticStateId<IdSet<WidgetId>> = StaticStateId::new_unique();
static MODAL_INCLUDED: StaticStateId<WidgetId> = StaticStateId::new_unique();