map_item() projection for index views

This commit is contained in:
Michael Sippel 2021-01-12 23:13:27 +01:00
parent 9c21ddbda1
commit f3fa8a7af1
Signed by: senvas
GPG key ID: F96CF119C34B64A6
3 changed files with 143 additions and 10 deletions

105
src/index/map_item.rs Normal file
View file

@ -0,0 +1,105 @@
pub use {
std::{
sync::{Arc, RwLock},
ops::Range
},
crate::{
core::{
View,
Observer,
ObserverBroadcast,
ViewPort,
InnerViewPort,
OuterViewPort
},
index::{IndexView}
}
};
impl<Key: 'static, Item: 'static> OuterViewPort<dyn IndexView<Key, Item = Item>> {
pub fn map_item<
DstItem: Default + 'static,
F: Fn(&Item) -> DstItem + Send + Sync + 'static
>(
&self,
f: F
) -> OuterViewPort<dyn IndexView<Key, Item = DstItem>> {
let port = ViewPort::new();
let map = MapIndexItem::new(port.inner(), f);
self.add_observer(map.clone());
port.into_outer()
}
}
pub struct MapIndexItem<Key, DstItem, SrcView, F>
where SrcView: IndexView<Key> + ?Sized,
F: Fn(&SrcView::Item) -> DstItem + Send + Sync
{
src_view: Option<Arc<SrcView>>,
f: F,
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<Key, Item = DstItem>>>>
}
impl<Key, DstItem, SrcView, F> MapIndexItem<Key, DstItem, SrcView, F>
where Key: 'static,
DstItem: Default + 'static,
SrcView: IndexView<Key> + ?Sized + 'static,
F: Fn(&SrcView::Item) -> DstItem + Send + Sync + 'static
{
fn new(
port: InnerViewPort<dyn IndexView<Key, Item = DstItem>>,
f: F
) -> Arc<RwLock<Self>> {
let map = Arc::new(RwLock::new(
MapIndexItem {
src_view: None,
f,
cast: port.get_broadcast()
}
));
port.set_view(Some(map.clone()));
map
}
}
impl<Key, DstItem, SrcView, F> View for MapIndexItem<Key, DstItem, SrcView, F>
where SrcView: IndexView<Key> + ?Sized,
F: Fn(&SrcView::Item) -> DstItem + Send + Sync
{
type Msg = Key;
}
impl<Key, DstItem, SrcView, F> IndexView<Key> for MapIndexItem<Key, DstItem, SrcView, F>
where DstItem: Default,
SrcView: IndexView<Key> + ?Sized,
F: Fn(&SrcView::Item) -> DstItem + Send + Sync
{
type Item = DstItem;
fn get(&self, key: &Key) -> Self::Item {
if let Some(v) = self.src_view.as_ref() {
(self.f)(&v.get(key))
} else {
DstItem::default()
}
}
fn range(&self) -> Option<Range<Key>> {
self.src_view.as_ref()?.range()
}
}
impl<Key, DstItem, SrcView, F> Observer<SrcView> for MapIndexItem<Key, DstItem, SrcView, F>
where SrcView: IndexView<Key> + ?Sized,
F: Fn(&SrcView::Item) -> DstItem + Send + Sync
{
fn reset(&mut self, view: Option<Arc<SrcView>>) {
// todo: notify on reset ??
self.src_view = view;
}
fn notify(&self, msg: &Key) {
self.cast.notify(msg);
}
}

View file

@ -1,4 +1,6 @@
pub mod map_item;
use {
std::{
sync::{Arc, RwLock},

View file

@ -2,15 +2,14 @@
#![feature(assoc_char_funcs)]
pub mod core;
pub mod view;
pub mod index;
pub mod grid;
pub mod sequence;
pub mod singleton;
pub mod terminal;
pub mod string_editor;
//pub mod singleton_buffer;
//pub mod vec_buffer;
//pub mod sequence_element_projection;
use {
async_std::{task},
std::{
@ -21,7 +20,7 @@ use {
termion::event::{Event, Key},
crate::{
core::{View, Observer, ObserverExt, ViewPort},
view::{*},
index::{ImplIndexView},
terminal::{
TerminalView,
TerminalAtom,
@ -30,6 +29,7 @@ use {
Terminal,
TerminalCompositor
},
grid::GridOffset
}
};
@ -77,7 +77,7 @@ impl ImplIndexView for ScrambleBackground {
type Value = Option<TerminalAtom>;
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
if ((pos.x/2) % 2 == 0) ^ ( pos.y % 2 == 0 ) {
if ((pos.x/2) % 2 == 0) ^ (pos.y % 2 == 0) {
Some(TerminalAtom::new(char::from((35+(5*pos.y+pos.x)%40) as u8), TerminalStyle::fg_color((40, 40, 40))))
} else {
Some(TerminalAtom::new(char::from((35+(pos.y+9*pos.x)%40) as u8), TerminalStyle::fg_color((90, 90, 90))))
@ -96,7 +96,7 @@ async fn main() {
let mut compositor = TerminalCompositor::new(term_port.inner());
compositor.push(ViewPort::<dyn TerminalView>::with_view(Arc::new(ScrambleBackground)).into_outer());
let mut term = Terminal::new(term_port.outer());
let term_writer = term.get_writer();
@ -107,11 +107,37 @@ async fn main() {
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
\*/
compositor.push(ViewPort::<dyn TerminalView>::with_view(Arc::new(Checkerboard)).into_outer());
let offset_port = ViewPort::<dyn TerminalView>::new();
let o = GridOffset::new(offset_port.inner());
let checkerboard_port = ViewPort::<dyn TerminalView>::with_view(Arc::new(Checkerboard));
checkerboard_port.add_observer(o.clone());
compositor.push(offset_port.into_outer());
let edit_port = ViewPort::<dyn TerminalView>::new();
let mut editor = string_editor::StringEditor::new(edit_port.inner());
compositor.push(edit_port.into_outer());
let edit_offset_port = ViewPort::<dyn TerminalView>::new();
let edit_o = GridOffset::new(edit_offset_port.inner());
edit_port.add_observer(edit_o.clone());
compositor.push(
edit_offset_port
.into_outer()
// add a nice black background
.map_item(|atom| atom.map(
|a| a.add_style_back(TerminalStyle::bg_color((0,0,0))))));
edit_o.write().unwrap().set_offset(Vector2::new(40, 4));
task::spawn(async move {
for x in 0 .. 20 {
async_std::task::sleep(std::time::Duration::from_millis(15)).await;
o.write().unwrap().set_offset(Vector2::new(x as i16, x as i16));
}
});
/*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
Event Loop