use crate::items_source::{unbox_item, ItemsChangedEventArgs, ItemsChangedHandler, ItemsSource};
use crate::utils::{CollectionChangedEventManager, ICollectionChangedListener};
use ferroui_base::utilities::HandlerList;
use ferroui_base::{BoxedValue, PropertyValue};
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::ops::Deref;
use std::rc::{Rc, Weak};
ferroui_base::ferro_thread_local! {
static EMPTY: Rc<ItemsSourceView> = ItemsSourceView::new(ItemsSource::from_items([]));
static UNINITIALIZED_SOURCE: ItemsSource = ItemsSource::from_items([]);
}
pub struct ItemsSourceView {
this: Weak<ItemsSourceView>,
source: RefCell<ItemsSource>,
collection_changed: HandlerList<ItemsChangedHandler>,
pre_collection_changed: HandlerList<ItemsChangedHandler>,
post_collection_changed: HandlerList<ItemsChangedHandler>,
listening: Cell<bool>,
}
impl ItemsSourceView {
pub fn empty() -> Rc<ItemsSourceView> {
EMPTY.with(Rc::clone)
}
pub(crate) fn uninitialized_source() -> ItemsSource {
UNINITIALIZED_SOURCE.with(Clone::clone)
}
pub(crate) fn new(source: ItemsSource) -> Rc<Self> {
Rc::new_cyclic(|this| Self {
this: this.clone(),
source: RefCell::new(source),
collection_changed: HandlerList::new(),
pre_collection_changed: HandlerList::new(),
post_collection_changed: HandlerList::new(),
listening: Cell::new(false),
})
}
#[inline]
pub fn count(&self) -> usize {
self.source.borrow().count()
}
#[inline]
pub fn source(&self) -> ItemsSource {
self.source.borrow().clone()
}
pub(crate) fn try_get_initialized_source(&self) -> Option<ItemsSource> {
let source = self.source();
if source.ptr_eq(&Self::uninitialized_source()) {
None
} else {
Some(source)
}
}
pub fn add_collection_changed(&self, handler: Rc<ItemsChangedHandler>) -> u64 {
self.add_listener_if_necessary();
self.collection_changed.add(handler)
}
pub fn remove_collection_changed(&self, token: u64) {
self.collection_changed.remove(token);
self.remove_listener_if_necessary();
}
pub(crate) fn add_pre_collection_changed(&self, handler: Rc<ItemsChangedHandler>) -> u64 {
self.add_listener_if_necessary();
self.pre_collection_changed.add(handler)
}
pub(crate) fn remove_pre_collection_changed(&self, token: u64) {
self.pre_collection_changed.remove(token);
self.remove_listener_if_necessary();
}
pub(crate) fn add_post_collection_changed(&self, handler: Rc<ItemsChangedHandler>) -> u64 {
self.add_listener_if_necessary();
self.post_collection_changed.add(handler)
}
pub(crate) fn remove_post_collection_changed(&self, token: u64) {
self.post_collection_changed.remove(token);
self.remove_listener_if_necessary();
}
#[inline]
pub fn get_at(&self, index: usize) -> Option<BoxedValue> {
let source = self.source.borrow().clone();
source.get_at(index)
}
pub fn contains(&self, item: &Option<BoxedValue>) -> bool {
self.index_of(item) >= 0
}
pub fn index_of(&self, item: &Option<BoxedValue>) -> i32 {
let source = self.source.borrow().clone();
source.index_of(item)
}
pub fn get_or_create(items: Option<&ItemsSource>) -> Rc<ItemsSourceView> {
match items {
None => Self::empty(),
Some(items) => Self::new(items.clone()),
}
}
pub fn get_or_create_of<T: PropertyValue>(items: Option<&ItemsSource>) -> ItemsSourceViewOf<T> {
match items {
None => ItemsSourceViewOf::empty(),
Some(items) => ItemsSourceViewOf::new(items.clone()),
}
}
pub fn iter(&self) -> impl Iterator<Item = Option<BoxedValue>> {
let source = self.source();
let mut index = 0;
std::iter::from_fn(move || {
if index < source.count() {
index += 1;
Some(source.get_at(index - 1))
} else {
None
}
})
}
pub fn to_vec(&self) -> Vec<Option<BoxedValue>> {
self.source().to_vec()
}
pub(crate) fn raise_collection_changed(&self, e: &ItemsChangedEventArgs<'_>) {
Self::invoke(&self.pre_collection_changed, e);
Self::invoke(&self.collection_changed, e);
Self::invoke(&self.post_collection_changed, e);
}
pub(crate) fn set_source(&self, source: ItemsSource) {
let listener: Weak<dyn ICollectionChangedListener> = self.this.clone();
if self.listening.get() {
let old = self.source();
if old.is_notifying() {
CollectionChangedEventManager::remove_listener(&old, &listener);
}
}
*self.source.borrow_mut() = source.clone();
if self.listening.get() && source.is_notifying() {
CollectionChangedEventManager::add_listener(&source, listener);
}
}
fn add_listener_if_necessary(&self) {
if !self.listening.get() {
let source = self.source();
if source.is_notifying() {
CollectionChangedEventManager::add_listener(&source, self.this.clone());
}
self.listening.set(true);
}
}
fn remove_listener_if_necessary(&self) {
if self.listening.get() && self.collection_changed.is_empty() && self.post_collection_changed.is_empty() {
let source = self.source();
if source.is_notifying() {
let listener: Weak<dyn ICollectionChangedListener> = self.this.clone();
CollectionChangedEventManager::remove_listener(&source, &listener);
}
self.listening.set(false);
}
}
#[inline]
fn invoke(handlers: &HandlerList<ItemsChangedHandler>, e: &ItemsChangedEventArgs<'_>) {
if !handlers.is_empty() {
for (_, handler) in handlers.snapshot().iter() {
handler(e);
}
}
}
}
impl ICollectionChangedListener for ItemsSourceView {
fn pre_changed(&self, e: &ItemsChangedEventArgs<'_>) {
Self::invoke(&self.pre_collection_changed, e);
}
fn changed(&self, e: &ItemsChangedEventArgs<'_>) {
Self::invoke(&self.collection_changed, e);
}
fn post_changed(&self, e: &ItemsChangedEventArgs<'_>) {
Self::invoke(&self.post_collection_changed, e);
}
}
pub struct ItemsSourceViewOf<T: PropertyValue> {
view: Rc<ItemsSourceView>,
marker: PhantomData<fn() -> T>,
}
impl<T: PropertyValue> Clone for ItemsSourceViewOf<T> {
fn clone(&self) -> Self {
Self { view: self.view.clone(), marker: PhantomData }
}
}
impl<T: PropertyValue> Deref for ItemsSourceViewOf<T> {
type Target = ItemsSourceView;
fn deref(&self) -> &ItemsSourceView {
&self.view
}
}
impl<T: PropertyValue> ItemsSourceViewOf<T> {
pub fn empty() -> Self {
Self { view: ItemsSourceView::empty(), marker: PhantomData }
}
pub(crate) fn new(source: ItemsSource) -> Self {
Self { view: ItemsSourceView::new(source), marker: PhantomData }
}
pub fn from_view(view: &ItemsSourceView) -> Self {
Self::new(view.source())
}
pub fn view(&self) -> &Rc<ItemsSourceView> {
&self.view
}
pub fn get_at(&self, index: usize) -> Option<T> {
let item = self.view.get_at(index);
if item.is_none() {
return None;
}
match unbox_item::<T>(&item) {
Some(value) => Some(value),
None => panic!("Unable to cast the item to type '{}'.", std::any::type_name::<T>()),
}
}
pub fn iter(&self) -> impl Iterator<Item = Option<T>> + '_ {
let mut index = 0;
std::iter::from_fn(move || {
if index < self.view.count() {
index += 1;
Some(self.get_at(index - 1))
} else {
None
}
})
}
}