use super::Modifier;
#[derive(Default)]
pub struct OverwriteBool {
modifiers: u32,
pub(crate) idx: u8,
len: u8,
needs_update: bool,
}
macro_rules! assert_overflow {
($index: expr) => {
debug_assert!(
$index < 32,
"If you ever see this,\
please open an issue at https://github.com/linebender/xilem/, \
we would appreciate to know what caused this. There are known solutions.\
This is currently limited to 32 booleans to be more efficient."
);
};
}
impl OverwriteBool {
#[inline]
pub fn rebuild(&mut self, prev_len: u8) {
self.idx -= prev_len;
}
fn set(&mut self, modifier: bool) -> bool {
let before = self.modifiers & (1 << self.idx);
if modifier {
self.modifiers |= 1 << self.idx;
} else {
self.modifiers &= !(1 << self.idx);
}
(self.modifiers & (1 << self.idx)) != before
}
fn get(&self) -> bool {
let bit = 1 << self.idx;
self.modifiers & bit == bit
}
#[inline]
pub fn push(this: &mut Modifier<'_, Self>, modifier: bool) {
debug_assert!(
this.flags.was_created(),
"This should never be called, when the underlying element wasn't (re)created."
);
this.modifier.set(modifier);
this.modifier.needs_update = true;
this.flags.set_needs_update();
this.modifier.idx += 1;
this.modifier.len += 1;
assert_overflow!(this.modifier.len);
}
#[inline]
pub fn mutate<R>(this: &mut Modifier<'_, Self>, f: impl FnOnce(&mut bool) -> R) -> R {
debug_assert!(
!this.flags.was_created(),
"This should never be called, when the underlying element was (re)created."
);
let mut modifier = this.modifier.get();
let retval = f(&mut modifier);
let dirty = this.modifier.set(modifier);
this.modifier.idx += 1;
this.modifier.needs_update |= this.modifier.len == this.modifier.idx && dirty;
if this.modifier.needs_update {
this.flags.set_needs_update();
}
retval
}
#[inline]
pub fn skip(this: &mut Modifier<'_, Self>, count: u8) {
debug_assert!(
!this.flags.was_created(),
"This should never be called, when the underlying element was (re)created."
);
this.modifier.idx += count;
}
#[inline]
pub fn update(this: &mut Modifier<'_, Self>, prev: bool, next: bool) {
if this.flags.was_created() {
Self::push(this, next);
} else if next != prev {
Self::mutate(this, |modifier| *modifier = next);
} else {
Self::skip(this, 1);
}
}
#[inline]
pub fn apply_changes(&mut self, f: impl FnOnce(Option<bool>)) {
let needs_update = self.needs_update;
self.needs_update = false;
if needs_update {
let bit = 1 << (self.idx - 1);
let modifier = (self.len > 0).then_some(self.modifiers & bit == bit);
f(modifier);
}
}
}
#[macro_export]
macro_rules! overwrite_bool_modifier {
($modifier: ident) => {
#[derive(Default)]
pub struct $modifier($crate::modifiers::OverwriteBool);
impl $modifier {
fn as_overwrite_bool_modifier(
this: $crate::modifiers::Modifier<'_, Self>,
) -> $crate::modifiers::Modifier<'_, $crate::modifiers::OverwriteBool> {
$crate::modifiers::Modifier::new(&mut this.modifier.0, this.flags)
}
pub fn apply_changes(&mut self, f: impl FnOnce(Option<bool>)) {
self.0.apply_changes(f);
}
}
};
}
#[macro_export]
macro_rules! overwrite_bool_modifier_view {
($modifier: ident) => {
pub struct $modifier<V, State, Action> {
value: bool,
inner: V,
phantom: std::marker::PhantomData<fn() -> (State, Action)>,
}
impl<V, State, Action> $modifier<V, State, Action> {
pub fn new(inner: V, value: bool) -> Self {
$modifier {
inner,
value,
phantom: std::marker::PhantomData,
}
}
}
impl<V, State, Action> $crate::core::ViewMarker for $modifier<V, State, Action> {}
impl<V, State, Action> $crate::core::View<State, Action, $crate::ViewCtx>
for $modifier<V, State, Action>
where
State: 'static,
Action: 'static,
V: $crate::DomView<
State,
Action,
Element: $crate::modifiers::WithModifier<super::$modifier>,
>,
for<'a> <V::Element as $crate::core::ViewElement>::Mut<'a>:
$crate::modifiers::WithModifier<super::$modifier>,
{
type Element = V::Element;
type ViewState = V::ViewState;
fn build(
&self,
ctx: &mut $crate::ViewCtx,
app_state: &mut State,
) -> (Self::Element, Self::ViewState) {
use $crate::modifiers::WithModifier;
let (mut el, state) = self.inner.build(ctx, app_state);
let modifier = &mut super::$modifier::as_overwrite_bool_modifier(el.modifier());
$crate::modifiers::OverwriteBool::push(modifier, self.value);
(el, state)
}
fn rebuild(
&self,
prev: &Self,
view_state: &mut Self::ViewState,
ctx: &mut $crate::ViewCtx,
mut element: $crate::core::Mut<'_, Self::Element>,
app_state: &mut State,
) {
use $crate::modifiers::WithModifier;
element.modifier().modifier.0.rebuild(1);
self.inner.rebuild(
&prev.inner,
view_state,
ctx,
element.reborrow_mut(),
app_state,
);
let mut modifier = super::$modifier::as_overwrite_bool_modifier(element.modifier());
$crate::modifiers::OverwriteBool::update(&mut modifier, prev.value, self.value);
}
fn teardown(
&self,
view_state: &mut Self::ViewState,
ctx: &mut $crate::ViewCtx,
element: $crate::core::Mut<'_, Self::Element>,
) {
self.inner.teardown(view_state, ctx, element);
}
fn message(
&self,
view_state: &mut Self::ViewState,
message: &mut $crate::core::MessageContext,
element: $crate::core::Mut<'_, Self::Element>,
app_state: &mut State,
) -> $crate::core::MessageResult<Action> {
self.inner.message(view_state, message, element, app_state)
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::PodFlags;
#[test]
fn overwrite_bool_push() {
let mut modifier = OverwriteBool::default();
let flags = &mut PodFlags::new(false);
let m = &mut Modifier::new(&mut modifier, flags);
assert!(!m.flags.needs_update());
OverwriteBool::push(m, true);
assert!(m.flags.needs_update());
assert_eq!(m.modifier.len, 1);
OverwriteBool::push(m, false);
assert!(m.flags.needs_update());
assert_eq!(m.modifier.len, 2);
assert_eq!(m.modifier.idx, 2);
let mut was_applied = false;
m.modifier.apply_changes(|value| {
was_applied = true;
assert_eq!(value, Some(false));
});
assert!(was_applied);
assert!(!m.modifier.needs_update);
}
#[test]
fn overwrite_bool_mutate() {
let mut modifier = OverwriteBool::default();
let flags = &mut PodFlags::new(false);
let m = &mut Modifier::new(&mut modifier, flags);
OverwriteBool::push(m, true);
OverwriteBool::push(m, false);
OverwriteBool::push(m, true);
OverwriteBool::push(m, false);
assert!(m.modifier.needs_update);
let mut was_applied = false;
m.modifier.apply_changes(|value| {
was_applied = true;
assert_eq!(value, Some(false));
});
assert!(was_applied);
m.flags.clear();
assert!(!m.flags.needs_update());
assert_eq!(m.modifier.len, 4);
assert_eq!(m.modifier.idx, 4);
m.modifier.rebuild(4);
assert_eq!(m.modifier.idx, 0);
assert_eq!(m.modifier.len, 4);
OverwriteBool::mutate(m, |first| *first = true);
assert!(!m.modifier.needs_update);
OverwriteBool::mutate(m, |second| *second = false);
assert!(!m.modifier.needs_update);
OverwriteBool::mutate(m, |third| *third = false);
assert!(!m.modifier.needs_update);
OverwriteBool::mutate(m, |fourth| *fourth = true);
assert!(m.modifier.needs_update);
let mut was_applied = false;
m.modifier.apply_changes(|value| {
was_applied = true;
assert_eq!(value, Some(true));
});
assert!(was_applied);
m.flags.clear();
assert!(!m.modifier.needs_update);
assert_eq!(m.modifier.len, 4);
assert_eq!(m.modifier.idx, 4);
}
#[test]
fn overwrite_bool_skip() {
let mut modifier = OverwriteBool::default();
let flags = &mut PodFlags::new(false);
let m = &mut Modifier::new(&mut modifier, flags);
OverwriteBool::push(m, true);
OverwriteBool::push(m, false);
OverwriteBool::push(m, false);
let mut was_applied = false;
m.modifier.apply_changes(|value| {
was_applied = true;
assert_eq!(value, Some(false));
});
assert!(was_applied);
m.flags.clear();
assert!(!m.modifier.needs_update);
assert_eq!(m.modifier.idx, 3);
m.modifier.rebuild(3);
assert_eq!(m.modifier.len, 3);
assert_eq!(m.modifier.idx, 0);
OverwriteBool::mutate(m, |first| *first = false); assert_eq!(m.modifier.idx, 1);
assert!(!m.modifier.needs_update);
OverwriteBool::skip(m, 2);
assert_eq!(m.modifier.len, 3);
assert_eq!(m.modifier.idx, 3);
assert!(!m.modifier.needs_update);
let mut was_applied = false;
m.modifier.apply_changes(|value| {
was_applied = true;
assert_eq!(value, Some(false));
});
m.flags.clear();
assert!(!was_applied);
}
#[test]
fn overwrite_bool_update() {
let mut modifier = OverwriteBool::default();
let flags = &mut PodFlags::new(false);
let m = &mut Modifier::new(&mut modifier, flags);
OverwriteBool::push(m, true);
OverwriteBool::push(m, false);
OverwriteBool::push(m, false);
let mut was_applied = false;
m.modifier.apply_changes(|value| {
was_applied = true;
assert_eq!(value, Some(false));
});
m.flags.clear();
assert!(was_applied);
assert!(!m.modifier.needs_update);
assert_eq!(m.modifier.idx, 3);
m.modifier.rebuild(3);
assert_eq!(m.modifier.len, 3);
assert_eq!(m.modifier.idx, 0);
assert_eq!(m.modifier.modifiers, 1);
OverwriteBool::update(m, true, false);
assert_eq!(m.modifier.idx, 1);
assert_eq!(m.modifier.modifiers, 0);
assert!(!m.modifier.needs_update);
OverwriteBool::update(m, false, true);
assert_eq!(m.modifier.idx, 2);
assert_eq!(m.modifier.modifiers, 1 << 1);
assert!(!m.modifier.needs_update);
OverwriteBool::update(m, false, true);
assert_eq!(m.modifier.modifiers, 3 << 1);
assert_eq!(m.modifier.idx, 3);
assert!(m.modifier.needs_update);
let mut was_applied = false;
m.modifier.apply_changes(|value| {
was_applied = true;
assert_eq!(value, Some(true));
});
m.flags.clear();
assert!(was_applied);
let mut modifier = OverwriteBool::default();
let flags = &mut PodFlags::new(false);
let modifier = &mut Modifier::new(&mut modifier, flags);
assert_eq!(modifier.modifier.len, 0);
assert_eq!(modifier.modifier.idx, 0);
OverwriteBool::update(modifier, false, true);
assert_eq!(modifier.modifier.idx, 1);
assert_eq!(modifier.modifier.modifiers, 1);
assert!(modifier.modifier.needs_update);
OverwriteBool::update(modifier, true, false);
OverwriteBool::update(modifier, true, false);
assert_eq!(modifier.modifier.len, 3);
assert_eq!(modifier.modifier.idx, 3);
assert_eq!(modifier.modifier.modifiers, 1);
let mut was_applied = false;
modifier.modifier.apply_changes(|value| {
was_applied = true;
assert_eq!(value, Some(false));
});
modifier.flags.clear();
assert!(was_applied);
}
#[test]
#[should_panic(
expected = "This should never be called, when the underlying element was (re)created."
)]
fn panic_if_use_mutate_on_creation() {
let mut modifier = OverwriteBool::default();
let flags = &mut PodFlags::new(false);
let m = &mut Modifier::new(&mut modifier, flags);
assert!(m.flags.was_created());
OverwriteBool::mutate(m, |m| *m = false);
}
#[test]
#[should_panic(
expected = "This should never be called, when the underlying element wasn't (re)created."
)]
fn panic_if_use_push_on_rebuild() {
let mut modifier = OverwriteBool::default();
let flags = &mut PodFlags::new(false);
let m = &mut Modifier::new(&mut modifier, flags);
assert!(m.flags.was_created());
OverwriteBool::push(m, true);
let mut was_applied = false;
m.modifier.apply_changes(|value| {
was_applied = true;
assert_eq!(value, Some(true));
});
assert!(was_applied);
m.flags.clear();
assert!(!m.flags.was_created());
OverwriteBool::push(m, true);
}
}