mod data_store;
use reexport::log;
use reexport::relm4;
use store::StoreViewMsg;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use relm4::Model as ViewModel;
use relm4::Sender;
use relm4::factory::FactoryListView;
use relm4::factory::FactoryView;
use record::Id;
use collections::WindowChangeset;
use collections::DataContainer;
use store::DataStore;
use store::StoreViewPrototype;
use store::Position;
use store::math::Range;
use store::window::StoreState;
use store::window::WindowBehavior;
use store::window::WindowTransition;
use super::widgets;
pub struct StoreViewImplementation<Configuration>
where
Configuration: ?Sized + StoreViewPrototype + 'static,
{
store: Configuration::Store,
#[allow(clippy::type_complexity)]
view: Rc<RefCell<DataContainer<<Configuration::Store as DataStore>::Record>>>,
#[allow(clippy::type_complexity)]
widgets: Rc<RefCell<HashMap<Id<<Configuration::Store as DataStore>::Record>, widgets::Widgets<Configuration::RecordWidgets, <Configuration::View as FactoryView<Configuration::Root>>::Root>>>>,
#[allow(clippy::type_complexity)]
changes: Rc<RefCell<Vec<StoreViewMsg<<Configuration::Store as DataStore>::Record>>>>,
range: Rc<RefCell<Range>>,
size: usize,
}
impl<Configuration> std::fmt::Debug for StoreViewImplementation<Configuration>
where
Configuration: ?Sized + StoreViewPrototype + 'static,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StoreViewImplementation")
.field("size", &self.size)
.field("range", &self.range)
.finish_non_exhaustive()
}
}
impl<Configuration> StoreViewImplementation<Configuration>
where
Configuration: ?Sized + StoreViewPrototype + 'static,
{
pub fn new(store: Configuration::Store, size: usize) -> Self {
let range = Rc::new(RefCell::new(Range::new(0, size)));
let changes = Rc::new(RefCell::new(Vec::new()));
changes.borrow_mut().push(StoreViewMsg::Reload);
Self{
store,
view: Rc::new(RefCell::new(DataContainer::new(size))),
widgets: Rc::new(RefCell::new(HashMap::new())),
changes,
range,
size,
}
}
pub fn inbox(&self, message: StoreViewMsg<<Configuration::Store as DataStore>::Record>) {
self.changes.borrow_mut().push(message);
}
fn convert_to_transition(&self, state: &StoreState<'_>, message: &StoreViewMsg<<Configuration::Store as DataStore>::Record>) -> WindowTransition {
match message {
StoreViewMsg::NewAt(p) => {
Configuration::Window::insert(state, &p.to_point())
},
StoreViewMsg::Move{from, to} => {
Configuration::Window::slide(state, &Range::new(from.0, to.0))
},
StoreViewMsg::Reorder{from, to} => {
Configuration::Window::slide(state, &Range::new(from.0, to.0))
},
StoreViewMsg::Remove(at) => {
Configuration::Window::remove(state, &at.to_point())
},
StoreViewMsg::Update(_) => {
WindowTransition::Identity
},
StoreViewMsg::Reload => {
WindowTransition::Identity
},
}
}
fn reload(&self, changeset: &mut WindowChangeset<<Configuration::Store as DataStore>::Record>) {
let range_of_changes = *self.range.borrow();
log::trace!("Range of changes {:?}", range_of_changes);
let new_records: Vec<<Configuration::Store as DataStore>::Record> = self.store.get_range(&range_of_changes);
log::trace!("New records length: {}", new_records.len());
let mut view = self.view.borrow_mut();
view.reload(changeset, new_records);
}
fn insert_right(&self, changeset: &mut WindowChangeset<<Configuration::Store as DataStore>::Record>, pos: usize, by: usize) {
let mut view = self.view.borrow_mut();
let range = self.range.borrow();
let range_of_changes = Range::new(pos, pos+by);
let data = self.store.get_range(&range_of_changes);
let position = pos - range.start();
view.insert_right(changeset, position, data);
}
fn insert_left(&self, changeset: &mut WindowChangeset<<Configuration::Store as DataStore>::Record>, pos: usize, by: usize) {
let mut view = self.view.borrow_mut();
let start = {
let range = self.range.borrow();
*range.start()
};
let range_of_changes = Range::new(pos, pos+by);
let data = self.store.get_range(&range_of_changes);
let position = pos - start;
if start == 0 && view.len() + by <= self.size {
view.insert_right(changeset, position, data);
}
else {
view.insert_left(changeset, position, data);
let new_range = {
let range = self.range.borrow();
range.to_right(by)
};
self.range.replace(new_range);
}
}
fn remove_right(&self, changeset: &mut WindowChangeset<<Configuration::Store as DataStore>::Record>, pos: usize, by: usize) {
let (range_start, range_end) = {
let range = self.range.borrow();
(*range.start(), *range.end())
};
let mut view = self.view.borrow_mut();
let position = pos - range_start;
let range_of_changes_start = if position + by > view.len() {
pos
}
else {
range_end - by
};
let range_of_changes = Range::new(
range_of_changes_start,
range_end
);
let data = self.store.get_range(&range_of_changes);
view.remove_right(changeset, position, by, data);
}
fn remove_left(&self, changeset: &mut WindowChangeset<<Configuration::Store as DataStore>::Record>, pos: usize, by: usize) {
log::trace!("Remove left");
log::trace!("\tPos: {}", pos);
log::trace!("\tBy: {}", by);
println!();
println!("Remove left");
println!("\tPos: {}", pos);
println!("\tBy: {}", by);
let (range_start, range_end) = {
let range = self.range.borrow();
(*range.start(), *range.end())
};
log::trace!("\tRange start: {}", range_start);
log::trace!("\tRange end: {}", range_end);
println!("\tRange start: {}", range_start);
println!("\tRange end: {}", range_end);
let mut view = self.view.borrow_mut();
let container_position = pos - range_start;
log::trace!("\tContainer position: {}", container_position);
println!("\tContainer position: {}", container_position);
let range_of_changes_start = if container_position < by {
0
}
else {
container_position - by
};
let range_of_changes = Range::new(
range_of_changes_start,
container_position );
let left_data = if !range_of_changes.is_empty() && range_start > 0 {
let range_of_changes_len = range_of_changes.len();
let left_range = if range_of_changes_len > range_start {
let length = range_of_changes_len - range_start;
Range::new(0, length)
}
else {
Range::new(range_start - range_of_changes_len, range_start)
};
println!("Left range: {:?}", left_range);
self.store.get_range(&left_range)
}
else {
vec![]
};
log::trace!("\tRange of changes: {}", range_of_changes);
log::trace!("\tLeft data: {:#?}", left_data);
println!("\tRange of changes: {}", range_of_changes);
println!("\tLeft data: {:#?}", left_data);
let new_range = Range::new(
range_start, range_end
).to_left(range_of_changes.len());
self.range.replace(new_range);
let right_data = if range_start < by {
let by_right = by - range_start;
let right_range = Range::new(
new_range.end() - by_right, *new_range.end()
);
self.store.get_range(&right_range)
}
else {
vec![]
};
log::trace!("\tRight data: {:#?}", right_data);
println!("\tRight data: {:#?}", right_data);
view.remove_left(changeset, container_position, by, left_data, right_data);
}
fn compile_changes(&self) -> WindowChangeset<<Configuration::Store as DataStore>::Record> {
let mut changeset = WindowChangeset::default();
for change in self.changes.borrow_mut().iter() {
let transition = {
let state = StoreState{
page: {
&self.range.borrow()
},
view: {
self.view.borrow().len()
},
};
self.convert_to_transition(&state, change)
};
match transition {
WindowTransition::Identity => {
match change {
StoreViewMsg::Update(id) => {
let mut view = self.view.borrow_mut();
if let Some(record) = self.store.get(id) {
changeset.update(*id);
view.update(record);
}
},
StoreViewMsg::Reload => {
log::trace!("Reload");
changeset.reload = true;
self.reload(&mut changeset);
},
_ => {}
}
},
WindowTransition::InsertLeft{pos, by } => {
log::trace!("Insert left");
self.insert_left(&mut changeset, pos, by);
}
WindowTransition::InsertRight{pos, by} => {
log::trace!("Insert right");
self.insert_right(&mut changeset, pos, by);
}
WindowTransition::RemoveLeft{pos, by} => {
log::trace!("RemoveLeft");
self.remove_left(&mut changeset, pos, by);
}
WindowTransition::RemoveRight{pos, by} => {
log::trace!("RemoveRight");
self.remove_right(&mut changeset, pos, by);
}
WindowTransition::SlideLeft(by) => {
log::trace!("SlideLeft");
let range = {
*self.range.borrow()
};
let start = *range.start();
let new_range = if start < by {
range.slide(0)
}
else {
range.slide(start - by)
};
self.range.replace(new_range);
self.reload(&mut changeset);
}
WindowTransition::SlideRight(by) => {
let exceeds = {
let range = self.range.borrow();
self.len() >= range.end() + by
};
if by > self.size || exceeds {
let new_range = {
let range = self.range.borrow();
let new_end = range.end() + by;
if new_end > self.len() {
if self.size > self.len() {
range.slide(self.len()-self.size)
}
else {
range.slide(0)
}
}
else {
range.slide(range.start()+by)
}
};
self.range.replace(new_range);
self.reload(&mut changeset);
}
else {
let new_range = {
let range = self.range.borrow();
range.slide(range.start() + by)
};
self.range.replace(new_range);
self.reload(&mut changeset);
}
}
}
}
self.changes.replace(vec![]);
for id in &changeset.ids_to_add {
changeset.ids_to_update.remove(id);
}
changeset
}
pub fn view(&self, view: &Configuration::View, sender: Sender<<Configuration::ViewModel as ViewModel>::Msg>) {
log::info!("[StoreViewImplementation::generate]");
let empty = {
let changes = self.changes.borrow();
changes.is_empty()
};
if empty {
return
}
let old_order = {
let view_order = self.view.borrow();
let iter = view_order.ordered_record_ids();
let mut v = Vec::with_capacity(view_order.len());
for id in iter { v.push(*id);
}
v
};
let old_order_len = old_order.len();
let WindowChangeset{
ids_to_remove,
ids_to_add,
ids_to_update,
reload: _,
} = self.compile_changes();
if ids_to_remove.is_empty() && ids_to_add.is_empty() && ids_to_update.is_empty() {
return
}
let mut widgets = self.widgets.borrow_mut();
let view_order = self.view.borrow();
log::trace!("[StoreViewImplementation::generate] view should have same length as data.\t\tview.len(): {}", view_order.len());
log::trace!("[StoreViewImplementation::generate] widgets should have same length as view.\twidgets.len(): {}", widgets.len());
log::trace!("[StoreViewImplementation::generate] Ordered record ids should have same length as view. \tview_order.ordered_record_ids(): {}", view_order.ordered_record_ids().len());
log::trace!("[StoreViewImplementation::generate] Changes should be empty. Is it? {}", self.changes.borrow().is_empty());
log::trace!("[StoreViewImplementation::generate]");
log::trace!("[StoreViewImplementation::generate] ids_to_add.len(): {}", ids_to_add.len());
log::trace!("[StoreViewImplementation::generate] ids_to_update.len(): {}", ids_to_update.len());
let mut position: Position = Position(*self.range.borrow().start());
let range = self.range.borrow();
for id in view_order.ordered_record_ids() {
if ids_to_add.contains(id) {
log::trace!("[StoreViewImplementation::generate] Id to add: {:?}", id);
if let Some(record) = self.get(id) {
log::trace!("[StoreViewImplementation::generate] Got record {:?}", record);
let new_widgets = Configuration::init_view(&record, position, sender.clone());
let widgets_root = Configuration::root_widget(&new_widgets);
let root = if widgets.is_empty() || position.0 == *range.start() {
log::trace!("[StoreViewImplementation::generate] Adding first element");
view.push_front(widgets_root)
}
else {
log::trace!("[StoreViewImplementation::generate] Adding non first element");
let prev_idx = (position - 1 - *range.start()).0;
log::info!("Index of previous elements: {}", prev_idx);
let prev_id = view_order.get_record_id_at((position - 1 - *range.start()).0);
let prev = widgets.get(prev_id).unwrap();
view.insert_after(widgets_root, &prev.root)
};
widgets.insert(
*id,
widgets::Widgets{
widgets: new_widgets,
root,
}
);
}
}
if ids_to_update.contains(id) {
if let Some(record) = self.get(id) {
if let Some( widget ) = widgets.get_mut(id) {
<Configuration as StoreViewPrototype>::view(record, position, &widget.widgets);
if old_order_len > position.0 && old_order[position.0] != *id {
if let Some(widget) = widgets.remove(id) {
view.remove(&widget.root);
let root = if position.0 == *range.start() {
view.push_front(Configuration::root_widget(&widget.widgets))
}
else {
let prev_idx = (position - 1 - *range.start()).0;
log::info!("Index of previous elements: {}", prev_idx);
let prev_id = view_order.get_record_id_at((position - 1 - *range.start()).0);
let prev = widgets.get(prev_id).unwrap();
view.insert_after(Configuration::root_widget(&widget.widgets), &prev.root)
};
widgets.insert(
*id,
widgets::Widgets{
widgets: widget.widgets,
root,
}
);
}
}
}
}
}
position = position + 1;
}
for id in ids_to_remove {
if let Some(widget) = widgets.remove(&id) {
view.remove(&widget.root);
}
}
log::trace!("after");
log::trace!("[StoreViewImplementation::generate] view should have same length as data.\t\tview.len(): {}", view_order.len());
log::trace!("[StoreViewImplementation::generate] widgets should have same length as view.\twidgets.len(): {}", widgets.len());
log::trace!("[StoreViewImplementation::generate] Should be empty. Is it? {}", self.changes.borrow().is_empty());
}
}