use crate::animation::request_draw;
use std::cell::{Cell, Ref, RefCell, RefMut};
use std::fmt;
use std::ops::{Deref, DerefMut};
pub struct DrawCell<T: Copy + PartialEq> {
inner: Cell<T>,
}
impl<T: Copy + PartialEq> DrawCell<T> {
pub const fn new(value: T) -> Self {
Self {
inner: Cell::new(value),
}
}
pub fn get(&self) -> T {
self.inner.get()
}
pub fn set(&self, value: T) {
if self.inner.get() != value {
self.inner.set(value);
request_draw();
}
}
pub fn replace(&self, value: T) -> T {
let old = self.inner.get();
if old != value {
self.inner.set(value);
request_draw();
}
old
}
}
impl<T: Copy + PartialEq + Default> Default for DrawCell<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: Copy + PartialEq + fmt::Debug> fmt::Debug for DrawCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("DrawCell").field(&self.inner.get()).finish()
}
}
impl<T: Copy + PartialEq> Clone for DrawCell<T> {
fn clone(&self) -> Self {
Self::new(self.inner.get())
}
}
pub struct DrawRefCell<T> {
inner: RefCell<T>,
}
impl<T> DrawRefCell<T> {
pub const fn new(value: T) -> Self {
Self {
inner: RefCell::new(value),
}
}
pub fn borrow(&self) -> Ref<'_, T> {
self.inner.borrow()
}
pub fn borrow_mut(&self) -> DrawRefMut<'_, T> {
DrawRefMut {
inner: self.inner.borrow_mut(),
}
}
}
impl<T: Default> Default for DrawRefCell<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: fmt::Debug> fmt::Debug for DrawRefCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("DrawRefCell").field(&self.inner).finish()
}
}
pub struct DrawRefMut<'a, T> {
inner: RefMut<'a, T>,
}
impl<T> Deref for DrawRefMut<'_, T> {
type Target = T;
fn deref(&self) -> &T {
&self.inner
}
}
impl<T> DerefMut for DrawRefMut<'_, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.inner
}
}
impl<T> Drop for DrawRefMut<'_, T> {
fn drop(&mut self) {
request_draw();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn set_to_new_value_requests_draw() {
let cell = DrawCell::new(1_i32);
crate::animation::clear_draw_request();
cell.set(2);
assert_eq!(cell.get(), 2);
assert!(
crate::animation::wants_draw(),
"changing a DrawCell must request a draw"
);
}
#[test]
fn set_to_same_value_does_not_request_draw() {
let cell = DrawCell::new(5_i32);
crate::animation::clear_draw_request();
cell.set(5);
assert_eq!(cell.get(), 5);
assert!(
!crate::animation::wants_draw(),
"a no-op write must NOT request a draw"
);
}
#[test]
fn replace_returns_old_and_requests_draw_only_on_change() {
let cell = DrawCell::new(10_i32);
crate::animation::clear_draw_request();
let old = cell.replace(11);
assert_eq!(old, 10);
assert_eq!(cell.get(), 11);
assert!(crate::animation::wants_draw());
crate::animation::clear_draw_request();
let old = cell.replace(11);
assert_eq!(old, 11);
assert!(!crate::animation::wants_draw(), "no-op replace stays quiet");
}
#[test]
fn draw_ref_cell_requests_draw_on_mut_borrow_drop() {
let cell = DrawRefCell::new(String::from("a"));
crate::animation::clear_draw_request();
assert_eq!(&*cell.borrow(), "a");
assert!(!crate::animation::wants_draw());
crate::animation::clear_draw_request();
{
let mut g = cell.borrow_mut();
g.push('b');
}
assert_eq!(&*cell.borrow(), "ab");
assert!(crate::animation::wants_draw());
}
}