optimize index views with IndexArea enum to allow range based notifications
This commit is contained in:
parent
bfd27fa3fa
commit
7fdc0bf272
23 changed files with 419 additions and 548 deletions
|
@ -1,18 +1,19 @@
|
|||
use {
|
||||
std::{
|
||||
sync::Arc,
|
||||
cmp::max,
|
||||
collections::HashMap
|
||||
},
|
||||
std::sync::RwLock,
|
||||
cgmath::{Point2, Vector2},
|
||||
crate::{
|
||||
core::{
|
||||
View, Observer, ObserverBroadcast, ObserverExt,
|
||||
View, Observer, ObserverBroadcast,
|
||||
ViewPort, InnerViewPort, OuterViewPort,
|
||||
port::UpdateTask
|
||||
},
|
||||
grid::{GridView, GridWindowIterator},
|
||||
index::IndexView,
|
||||
index::{IndexArea, IndexView},
|
||||
projection::ProjectionHelper
|
||||
}
|
||||
};
|
||||
|
@ -48,7 +49,7 @@ where Item: 'static
|
|||
impl<Item> View for Flatten<Item>
|
||||
where Item: 'static
|
||||
{
|
||||
type Msg = Point2<i16>;
|
||||
type Msg = IndexArea<Point2<i16>>;
|
||||
}
|
||||
|
||||
impl<Item> IndexView<Point2<i16>> for Flatten<Item>
|
||||
|
@ -62,8 +63,10 @@ where Item: 'static
|
|||
chunk.view.get(&(*idx - chunk.offset))
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Point2<i16>>> {
|
||||
Some(GridWindowIterator::from(Point2::new(0, 0) .. self.limit).collect())
|
||||
fn area(&self) -> IndexArea<Point2<i16>> {
|
||||
IndexArea::Range(
|
||||
Point2::new(0, 0) ..= self.limit
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,8 +86,10 @@ where Item: 'static
|
|||
top: proj_helper.new_index_arg(
|
||||
Point2::new(-1, -1),
|
||||
top_port,
|
||||
|s: &mut Self, chunk_idx| {
|
||||
s.update_chunk(*chunk_idx);
|
||||
|s: &mut Self, chunk_area| {
|
||||
for chunk_idx in chunk_area.iter() {
|
||||
s.update_chunk(chunk_idx);
|
||||
}
|
||||
}
|
||||
),
|
||||
chunks: HashMap::new(),
|
||||
|
@ -104,20 +109,26 @@ where Item: 'static
|
|||
let view = self.proj_helper.new_index_arg(
|
||||
chunk_idx,
|
||||
chunk_port.clone(),
|
||||
move |s: &mut Self, idx| {
|
||||
move |s: &mut Self, area| {
|
||||
if let Some(chunk) = s.chunks.get(&chunk_idx) {
|
||||
if chunk.limit != chunk.view.range().end {
|
||||
if chunk.limit != *chunk.view.area().range().end() {
|
||||
s.update_all_offsets();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(chunk) = s.chunks.get(&chunk_idx) {
|
||||
s.cast.notify(&(idx + chunk.offset));
|
||||
s.cast.notify(
|
||||
&area.map(|pt| pt + chunk.offset)
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if let Some(chunk) = self.chunks.get_mut(&chunk_idx) {
|
||||
chunk.view = view;
|
||||
self.cast.notify(
|
||||
&chunk.view.area().map(|pt| pt + chunk.offset)
|
||||
);
|
||||
} else {
|
||||
self.chunks.insert(
|
||||
chunk_idx,
|
||||
|
@ -129,94 +140,91 @@ where Item: 'static
|
|||
);
|
||||
}
|
||||
|
||||
chunk_port.0.update();
|
||||
self.update_all_offsets();
|
||||
chunk_port.0.update();
|
||||
} else {
|
||||
self.proj_helper.remove_arg(&chunk_idx);
|
||||
|
||||
let mut dirty_idx = Vec::new();
|
||||
if let Some(chunk) = self.chunks.get_mut(&chunk_idx) {
|
||||
dirty_idx.extend(
|
||||
GridWindowIterator::from(
|
||||
Point2::new(chunk.offset.x, chunk.offset.y)
|
||||
.. Point2::new(chunk.offset.x + chunk.limit.x, chunk.offset.y + chunk.limit.y))
|
||||
);
|
||||
if let Some(chunk) = self.chunks.remove(&chunk_idx) {
|
||||
self.update_all_offsets();
|
||||
}
|
||||
|
||||
self.chunks.remove(&chunk_idx);
|
||||
self.cast.notify_each(dirty_idx);
|
||||
self.update_all_offsets();
|
||||
}
|
||||
}
|
||||
|
||||
/// recalculate all chunk offsets
|
||||
/// and update size of flattened grid
|
||||
fn update_all_offsets(&mut self) {
|
||||
let mut dirty_idx = Vec::new();
|
||||
let top_range = self.top.area().range();
|
||||
let mut col_widths = vec![0 as i16; (top_range.end().x+1) as usize];
|
||||
let mut row_heights = vec![0 as i16; (top_range.end().y+1) as usize];
|
||||
|
||||
let top_range = self.top.range();
|
||||
let mut col_widths = vec![0 as i16; (top_range.end.x) as usize];
|
||||
let mut row_heights = vec![0 as i16; (top_range.end.y) as usize];
|
||||
|
||||
for chunk_idx in GridWindowIterator::from(self.top.range()) {
|
||||
for chunk_idx in GridWindowIterator::from(top_range.clone()) {
|
||||
if let Some(chunk) = self.chunks.get_mut(&chunk_idx) {
|
||||
let lim = chunk.view.range().end;
|
||||
col_widths[chunk_idx.x as usize] = std::cmp::max(col_widths[chunk_idx.x as usize], lim.x);
|
||||
row_heights[chunk_idx.y as usize] = std::cmp::max(row_heights[chunk_idx.y as usize], lim.y);
|
||||
let chunk_range = chunk.view.area().range();
|
||||
let lim = *chunk_range.end();
|
||||
|
||||
col_widths[chunk_idx.x as usize] =
|
||||
max(
|
||||
col_widths[chunk_idx.x as usize],
|
||||
if lim.x < 0 { 0 } else { lim.x+1 }
|
||||
);
|
||||
row_heights[chunk_idx.y as usize] =
|
||||
max(
|
||||
row_heights[chunk_idx.y as usize],
|
||||
if lim.y < 0 { 0 } else { lim.y+1 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for chunk_idx in GridWindowIterator::from(self.top.range()) {
|
||||
for chunk_idx in GridWindowIterator::from(top_range.clone()) {
|
||||
if let Some(chunk) = self.chunks.get_mut(&chunk_idx) {
|
||||
let old_offset = chunk.offset;
|
||||
let old_limit = chunk.limit;
|
||||
|
||||
chunk.limit = chunk.view.range().end;
|
||||
chunk.limit = *chunk.view.area().range().end();
|
||||
chunk.offset = Vector2::new(
|
||||
(0 .. chunk_idx.x as usize).map(|x| col_widths[x]).sum(),
|
||||
(0 .. chunk_idx.y as usize).map(|y| row_heights[y]).sum()
|
||||
);
|
||||
/*
|
||||
if old_offset != chunk.offset {
|
||||
dirty_idx.extend(
|
||||
GridWindowIterator::from(
|
||||
Point2::new(std::cmp::min(old_offset.x, chunk.offset.x),
|
||||
std::cmp::min(old_offset.y, chunk.offset.y))
|
||||
.. Point2::new(std::cmp::max(old_offset.x + old_limit.x, chunk.offset.x + chunk.limit.x),
|
||||
std::cmp::max(old_offset.y + old_limit.y, chunk.offset.y + chunk.limit.y)))
|
||||
self.cast.notify(
|
||||
&IndexArea::Range(
|
||||
Point2::new(
|
||||
std::cmp::min(old_offset.x, chunk.offset.x),
|
||||
std::cmp::min(old_offset.y, chunk.offset.y)
|
||||
)
|
||||
..= Point2::new(
|
||||
std::cmp::max(old_offset.x + old_limit.x, chunk.offset.x + chunk.limit.x),
|
||||
std::cmp::max(old_offset.y + old_limit.y, chunk.offset.y + chunk.limit.y)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
let old_limit = self.limit;
|
||||
self.limit = Point2::new(
|
||||
(0 .. top_range.end.x as usize).map(|x| col_widths[x]).sum(),
|
||||
(0 .. top_range.end.y as usize).map(|y| row_heights[y]).sum()
|
||||
(0 ..= top_range.end().x as usize).map(|x| col_widths[x]).sum::<i16>() - 1,
|
||||
(0 ..= top_range.end().y as usize).map(|y| row_heights[y]).sum::<i16>() - 1,
|
||||
);
|
||||
|
||||
// fixme: dirty hack to mitigate the buggy notifications, not efficien
|
||||
dirty_idx.extend(
|
||||
GridWindowIterator::from(
|
||||
Point2::new(0, 0) .. Point2::new(
|
||||
std::cmp::max(old_limit.x, self.limit.x),
|
||||
std::cmp::max(old_limit.y, self.limit.y)
|
||||
)
|
||||
self.cast.notify(
|
||||
&IndexArea::Range(
|
||||
Point2::new(0, 0) ..= Point2::new(max(self.limit.x, old_limit.x), max(self.limit.y, old_limit.y))
|
||||
)
|
||||
);
|
||||
|
||||
self.cast.notify_each(dirty_idx);
|
||||
}
|
||||
|
||||
/// given an index in the flattened sequence,
|
||||
/// which sub-sequence does it belong to?
|
||||
fn get_chunk_idx(&self, glob_pos: Point2<i16>) -> Option<Point2<i16>> {
|
||||
for chunk_idx in GridWindowIterator::from(self.top.range()) {
|
||||
for chunk_idx in GridWindowIterator::from(self.top.area().range()) {
|
||||
if let Some(chunk) = self.chunks.get(&chunk_idx) {
|
||||
let chunk_range = chunk.view.range();
|
||||
let end = chunk_range.end + chunk.offset;
|
||||
let end = chunk.limit + chunk.offset;
|
||||
|
||||
if glob_pos.x < end.x && glob_pos.y < end.y {
|
||||
if glob_pos.x <= end.x && glob_pos.y <= end.y {
|
||||
return Some(chunk_idx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,81 +1,81 @@
|
|||
|
||||
use {
|
||||
std::{
|
||||
ops::{Range, RangeInclusive}
|
||||
ops::RangeInclusive,
|
||||
cmp::{min, max}
|
||||
},
|
||||
cgmath::{Point2},
|
||||
crate::{
|
||||
index::{IndexView}
|
||||
}
|
||||
crate::index::{IndexArea, IndexView}
|
||||
};
|
||||
|
||||
pub mod offset;
|
||||
pub mod flatten;
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub trait GridView = IndexView<Point2<i16>>;
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<Item> dyn GridView<Item = Item> {
|
||||
pub fn range(&self) -> Range<Point2<i16>> {
|
||||
if let Some(area) = self.area() {
|
||||
Point2::new(
|
||||
area.iter().map(|p| p.x).min().unwrap_or(0),
|
||||
area.iter().map(|p| p.y).min().unwrap_or(0)
|
||||
) ..
|
||||
Point2::new(
|
||||
area.iter().map(|p| p.x+1).max().unwrap_or(0),
|
||||
area.iter().map(|p| p.y+1).max().unwrap_or(0)
|
||||
)
|
||||
} else {
|
||||
Point2::new(i16::MIN, i16::MIN) .. Point2::new(i16::MAX, i16::MAX)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub mod offset;
|
||||
pub mod flatten;
|
||||
pub mod window_iterator;
|
||||
|
||||
pub use window_iterator::GridWindowIterator;
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct GridWindowIterator {
|
||||
next: Point2<i16>,
|
||||
range: Range<Point2<i16>>
|
||||
}
|
||||
impl IndexArea<Point2<i16>> {
|
||||
|
||||
impl From<Range<Point2<i16>>> for GridWindowIterator {
|
||||
fn from(range: Range<Point2<i16>>) -> Self {
|
||||
GridWindowIterator {
|
||||
next: range.start,
|
||||
range
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeInclusive<Point2<i16>>> for GridWindowIterator {
|
||||
fn from(range: RangeInclusive<Point2<i16>>) -> Self {
|
||||
GridWindowIterator {
|
||||
next: *range.start(),
|
||||
range: *range.start() .. Point2::new(range.end().x+1, range.end().y+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for GridWindowIterator {
|
||||
type Item = Point2<i16>;
|
||||
|
||||
fn next(&mut self) -> Option<Point2<i16>> {
|
||||
if self.next.y < self.range.end.y {
|
||||
let next = self.next;
|
||||
|
||||
if self.next.x+1 < self.range.end.x {
|
||||
self.next.x += 1;
|
||||
} else {
|
||||
self.next.x = self.range.start.x;
|
||||
self.next.y += 1;
|
||||
}
|
||||
|
||||
Some(next)
|
||||
} else {
|
||||
None
|
||||
// todo: this is not perfect (e.g. diagonals are inefficient)
|
||||
pub fn iter(&self) -> GridWindowIterator {
|
||||
GridWindowIterator::from(self.range())
|
||||
}
|
||||
|
||||
pub fn range(&self) -> RangeInclusive<Point2<i16>> {
|
||||
match self {
|
||||
IndexArea::Empty => Point2::new(i16::MAX, i16::MAX) ..= Point2::new(i16::MIN, i16::MIN),
|
||||
IndexArea::Full => panic!("range from full grid area"),
|
||||
IndexArea::Set(v) =>
|
||||
Point2::new(
|
||||
v.iter().map(|p| p.x).min().unwrap_or(0),
|
||||
v.iter().map(|p| p.y).min().unwrap_or(0)
|
||||
) ..=
|
||||
Point2::new(
|
||||
v.iter().map(|p| p.x).max().unwrap_or(0),
|
||||
v.iter().map(|p| p.y).max().unwrap_or(0)
|
||||
),
|
||||
IndexArea::Range(r) => r.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn union(self, other: IndexArea<Point2<i16>>) -> IndexArea<Point2<i16>> {
|
||||
match (self, other) {
|
||||
(IndexArea::Empty, a) |
|
||||
(a, IndexArea::Empty) => a,
|
||||
|
||||
(IndexArea::Full, _) |
|
||||
(_, IndexArea::Full) => IndexArea::Full,
|
||||
|
||||
(IndexArea::Set(mut va), IndexArea::Set(mut vb)) => {
|
||||
va.extend(vb.into_iter());
|
||||
IndexArea::Set(va)
|
||||
},
|
||||
|
||||
(IndexArea::Range(r), IndexArea::Set(mut v)) |
|
||||
(IndexArea::Set(mut v), IndexArea::Range(r)) => {
|
||||
v.extend(GridWindowIterator::from(r));
|
||||
IndexArea::Set(v)
|
||||
},
|
||||
|
||||
(IndexArea::Range(ra), IndexArea::Range(rb)) => IndexArea::Range(
|
||||
Point2::new(
|
||||
min(ra.start().x, rb.start().x),
|
||||
min(ra.start().y, rb.start().y)
|
||||
)
|
||||
..=
|
||||
Point2::new(
|
||||
max(ra.end().x, rb.end().x),
|
||||
max(ra.end().y, rb.end().y)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
60
nested/src/grid/window_iterator.rs
Normal file
60
nested/src/grid/window_iterator.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
|
||||
use {
|
||||
std::ops::{Range, RangeInclusive},
|
||||
cgmath::{Point2}
|
||||
};
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct GridWindowIterator {
|
||||
next: Point2<i16>,
|
||||
range: Range<Point2<i16>>
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl From<Range<Point2<i16>>> for GridWindowIterator {
|
||||
fn from(range: Range<Point2<i16>>) -> Self {
|
||||
GridWindowIterator {
|
||||
next: range.start,
|
||||
range
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeInclusive<Point2<i16>>> for GridWindowIterator {
|
||||
fn from(range: RangeInclusive<Point2<i16>>) -> Self {
|
||||
GridWindowIterator {
|
||||
next: *range.start(),
|
||||
range: *range.start() .. Point2::new(range.end().x+1, range.end().y+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl Iterator for GridWindowIterator {
|
||||
type Item = Point2<i16>;
|
||||
|
||||
fn next(&mut self) -> Option<Point2<i16>> {
|
||||
if self.next.y < self.range.end.y {
|
||||
if self.next.x < self.range.end.x {
|
||||
let next = self.next;
|
||||
|
||||
if self.next.x+1 < self.range.end.x {
|
||||
self.next.x += 1;
|
||||
} else {
|
||||
self.next.x = self.range.start.x;
|
||||
self.next.y += 1;
|
||||
}
|
||||
|
||||
Some(next)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ use {
|
|||
View,
|
||||
InnerViewPort
|
||||
},
|
||||
index::IndexView
|
||||
index::{IndexArea, IndexView}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -25,7 +25,7 @@ impl<Key, Item> View for IndexBufferView<Key, Item>
|
|||
where Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||
Item: Clone + Send + Sync + 'static
|
||||
{
|
||||
type Msg = Key;
|
||||
type Msg = IndexArea<Key>;
|
||||
}
|
||||
|
||||
impl<Key, Item> IndexView<Key> for IndexBufferView<Key, Item>
|
||||
|
@ -38,12 +38,11 @@ where Key: Clone + Hash + Eq + Send + Sync + 'static,
|
|||
self.0.read().unwrap().get(key).cloned()
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Key>> {
|
||||
Some(self.0.read().unwrap().keys().cloned().collect())
|
||||
fn area(&self) -> IndexArea<Key> {
|
||||
IndexArea::Set(self.0.read().unwrap().keys().cloned().collect())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct IndexBuffer<Key, Item>
|
||||
where Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||
Item: Clone + Send + Sync + 'static
|
||||
|
@ -68,7 +67,7 @@ where Key: Clone + Hash + Eq + Send + Sync + 'static,
|
|||
|
||||
pub fn insert(&mut self, key: Key, item: Item) {
|
||||
self.data.write().unwrap().insert(key.clone(), item);
|
||||
self.cast.notify(&key);
|
||||
self.cast.notify(&IndexArea::Set(vec![ key ]));
|
||||
}
|
||||
|
||||
pub fn insert_iter<T>(&mut self, iter: T)
|
||||
|
@ -80,7 +79,7 @@ where Key: Clone + Hash + Eq + Send + Sync + 'static,
|
|||
|
||||
pub fn remove(&mut self, key: Key) {
|
||||
self.data.write().unwrap().remove(&key);
|
||||
self.cast.notify(&key);
|
||||
self.cast.notify(&IndexArea::Set(vec![ key ]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ pub use {
|
|||
InnerViewPort,
|
||||
OuterViewPort
|
||||
},
|
||||
index::{IndexView}
|
||||
index::{IndexArea, IndexView}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -76,7 +76,7 @@ where Key: Clone + Send + Sync,
|
|||
SrcView: IndexView<Key> + ?Sized,
|
||||
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync
|
||||
{
|
||||
type Msg = Key;
|
||||
type Msg = IndexArea<Key>;
|
||||
}
|
||||
|
||||
impl<Key, DstItem, SrcView, F> IndexView<Key> for MapIndexItem<Key, DstItem, SrcView, F>
|
||||
|
@ -90,7 +90,7 @@ where Key: Clone + Send + Sync,
|
|||
self.src_view.get(key).as_ref().map(|item| (self.f)(key, item))
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Key>> {
|
||||
fn area(&self) -> IndexArea<Key> {
|
||||
self.src_view.area()
|
||||
}
|
||||
}
|
||||
|
@ -102,15 +102,15 @@ where Key: Clone + Send + Sync,
|
|||
{
|
||||
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||
let old_area = self.area();
|
||||
self.src_view = view;
|
||||
let new_area = self.area();
|
||||
|
||||
if let Some(area) = old_area { self.cast.notify_each(area); }
|
||||
if let Some(area) = new_area { self.cast.notify_each(area); }
|
||||
self.src_view = view;
|
||||
|
||||
self.cast.notify(&old_area);
|
||||
self.cast.notify(&self.src_view.area())
|
||||
}
|
||||
|
||||
fn notify(&mut self, msg: &Key) {
|
||||
self.cast.notify(msg);
|
||||
fn notify(&mut self, area: &IndexArea<Key>) {
|
||||
self.cast.notify(area);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ pub use {
|
|||
InnerViewPort,
|
||||
OuterViewPort
|
||||
},
|
||||
index::{IndexView},
|
||||
index::{IndexArea, IndexView},
|
||||
grid::{GridView}
|
||||
}
|
||||
};
|
||||
|
@ -106,7 +106,7 @@ where DstKey: Clone + Send + Sync,
|
|||
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
|
||||
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
|
||||
{
|
||||
type Msg = DstKey;
|
||||
type Msg = IndexArea<DstKey>;
|
||||
}
|
||||
|
||||
impl<DstKey, SrcKey, SrcView, F1, F2> IndexView<DstKey> for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
|
||||
|
@ -122,8 +122,8 @@ where DstKey: Clone + Send + Sync,
|
|||
self.src_view.get(&(self.f2)(key)?)
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<DstKey>> {
|
||||
Some(self.src_view.area()?.iter().map(&self.f1).collect())
|
||||
fn area(&self) -> IndexArea<DstKey> {
|
||||
self.src_view.area().map(&self.f1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,14 +137,12 @@ where DstKey: Clone + Send + Sync,
|
|||
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||
let old_area = self.area();
|
||||
self.src_view = view;
|
||||
let new_area = self.area();
|
||||
|
||||
if let Some(area) = old_area { self.cast.notify_each(area); }
|
||||
if let Some(area) = new_area { self.cast.notify_each(area); }
|
||||
self.cast.notify(&old_area);
|
||||
self.cast.notify(&self.area());
|
||||
}
|
||||
|
||||
fn notify(&mut self, msg: &SrcKey) {
|
||||
self.cast.notify(&(self.f1)(msg));
|
||||
fn notify(&mut self, msg: &IndexArea<SrcKey>) {
|
||||
self.cast.notify(&msg.map(&self.f1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ pub mod buffer;
|
|||
use {
|
||||
std::{
|
||||
sync::Arc,
|
||||
ops::Deref,
|
||||
ops::{Deref, RangeInclusive},
|
||||
},
|
||||
std::sync::RwLock,
|
||||
crate::core::View
|
||||
|
@ -14,15 +14,34 @@ use {
|
|||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub trait IndexView<Key> : View<Msg = Key>
|
||||
#[derive(Clone)]
|
||||
pub enum IndexArea<Key> {
|
||||
Empty,
|
||||
Full,
|
||||
Set(Vec<Key>),
|
||||
Range(RangeInclusive<Key>),
|
||||
//Procedural(Arc<dyn Fn() -> Box<dyn Iterator<Item = Key>>>)
|
||||
}
|
||||
|
||||
impl<Key> IndexArea<Key> {
|
||||
pub fn map<T>(&self, f: impl Fn(&Key) -> T) -> IndexArea<T> {
|
||||
match self {
|
||||
IndexArea::Empty => IndexArea::Empty,
|
||||
IndexArea::Full => IndexArea::Full,
|
||||
IndexArea::Set(v) => IndexArea::Set(v.iter().map(&f).collect()),
|
||||
IndexArea::Range(r) => IndexArea::Range(f(&r.start()) ..= f(&r.end()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IndexView<Key> : View<Msg = IndexArea<Key>>
|
||||
where Key: Send + Sync {
|
||||
type Item;
|
||||
|
||||
fn get(&self, key: &Key) -> Option<Self::Item>;
|
||||
|
||||
// todo: AreaIterator enum to switch between Allocated and Procedural area
|
||||
fn area(&self) -> Option<Vec<Key>> {
|
||||
None
|
||||
fn area(&self) -> IndexArea<Key> {
|
||||
IndexArea::Full
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +57,7 @@ where Key: Send + Sync,
|
|||
self.read().unwrap().get(key)
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Key>> {
|
||||
fn area(&self) -> IndexArea<Key> {
|
||||
self.read().unwrap().area()
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +72,7 @@ where Key: Send + Sync,
|
|||
self.deref().get(key)
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Key>> {
|
||||
fn area(&self) -> IndexArea<Key> {
|
||||
self.deref().area()
|
||||
}
|
||||
}
|
||||
|
@ -68,17 +87,17 @@ where Key: Send + Sync,
|
|||
self.as_ref()?.get(key)
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Key>> {
|
||||
fn area(&self) -> IndexArea<Key> {
|
||||
if let Some(v) = self.as_ref() {
|
||||
v.area()
|
||||
} else {
|
||||
Some(Vec::new())
|
||||
IndexArea::Empty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
/*
|
||||
pub trait ImplIndexView : Send + Sync {
|
||||
type Key : Send + Sync;
|
||||
type Value;
|
||||
|
@ -104,4 +123,4 @@ impl<V: ImplIndexView> IndexView<V::Key> for V {
|
|||
(self as &V).area()
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
use {
|
||||
std::{
|
||||
sync::{Arc}
|
||||
},
|
||||
std::sync::RwLock,
|
||||
cgmath::Point2,
|
||||
crate::{
|
||||
core::{ViewPort, Observer, ObserverExt, ObserverBroadcast, InnerViewPort, OuterViewPort},
|
||||
index::{ImplIndexView},
|
||||
terminal::{TerminalAtom, TerminalView, TerminalStyle},
|
||||
projection::{ProjectionHelper}
|
||||
}
|
||||
};
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct LeveledTermView {
|
||||
src: Arc<RwLock<dyn TerminalView>>,
|
||||
level: usize,
|
||||
|
||||
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>,
|
||||
proj_helper: ProjectionHelper<(), Self>
|
||||
}
|
||||
|
||||
impl LeveledTermView {
|
||||
pub fn new(
|
||||
src: OuterViewPort<dyn TerminalView>
|
||||
) -> (Arc<RwLock<Self>>, OuterViewPort<dyn TerminalView>) {
|
||||
let port = ViewPort::new();
|
||||
let v = Self::with_port(src, port.inner());
|
||||
(v, port.into_outer())
|
||||
}
|
||||
|
||||
pub fn with_port(
|
||||
src_port: OuterViewPort<dyn TerminalView>,
|
||||
dst_port: InnerViewPort<dyn TerminalView>
|
||||
) -> Arc<RwLock<Self>> {
|
||||
let mut proj_helper = ProjectionHelper::new(dst_port.0.update_hooks.clone());
|
||||
|
||||
let v = Arc::new(RwLock::new(
|
||||
LeveledTermView {
|
||||
src: proj_helper.new_index_arg(
|
||||
(),
|
||||
src_port,
|
||||
|p: &mut Self, pos: &Point2<i16>| {
|
||||
p.cast.notify(pos);
|
||||
}),
|
||||
level: 0,
|
||||
cast: dst_port.get_broadcast(),
|
||||
proj_helper
|
||||
}
|
||||
));
|
||||
|
||||
v.write().unwrap().proj_helper.set_proj(&v);
|
||||
dst_port.set_view(Some(v.clone()));
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
pub fn set_level(&mut self, l: usize) {
|
||||
if self.level != l {
|
||||
self.level = l;
|
||||
|
||||
// update complete area
|
||||
if let Some(a) = self.src.area() {
|
||||
self.cast.notify_each(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplIndexView for LeveledTermView {
|
||||
type Key = Point2<i16>;
|
||||
type Value = TerminalAtom;
|
||||
|
||||
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
|
||||
self.src.get(pos).map(
|
||||
|a| a.add_style_front(
|
||||
if self.level > 1 {
|
||||
TerminalStyle::bold(true)
|
||||
.add(TerminalStyle::bg_color((0, 0, 0)))
|
||||
} else if self.level > 0 {
|
||||
TerminalStyle::bg_color((40, 40, 40))
|
||||
} else {
|
||||
TerminalStyle::bold(false)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Point2<i16>>> {
|
||||
self.src.area()
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,6 @@ pub mod list;
|
|||
pub mod tree_nav;
|
||||
|
||||
pub mod string_editor;
|
||||
pub mod leveled_term_view;
|
||||
|
||||
pub mod bimap;
|
||||
|
||||
|
|
|
@ -3,22 +3,9 @@ use {
|
|||
termion::event::{Event, Key},
|
||||
crate::{
|
||||
core::{
|
||||
View,
|
||||
ViewPort,
|
||||
OuterViewPort,
|
||||
InnerViewPort,
|
||||
ObserverBroadcast,
|
||||
Observer,
|
||||
ObserverExt,
|
||||
context::{
|
||||
ReprTree,
|
||||
Object,
|
||||
MorphismType,
|
||||
MorphismMode,
|
||||
Context
|
||||
}
|
||||
},
|
||||
projection::ProjectionHelper,
|
||||
singleton::{SingletonView, SingletonBuffer},
|
||||
sequence::{SequenceView},
|
||||
vec::{VecBuffer},
|
||||
|
@ -30,7 +17,6 @@ use {
|
|||
TerminalEditorResult,
|
||||
make_label
|
||||
},
|
||||
leveled_term_view::LeveledTermView,
|
||||
list::{SExprView, ListDecoration, ListCursor, ListCursorMode, editor_view::{ListEditorView, ListEditorViewSegment}},
|
||||
tree_nav::{TreeCursor, TreeNav, TreeNavResult, TerminalTreeEditor}
|
||||
}
|
||||
|
@ -61,6 +47,7 @@ where ItemEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
|
|||
|
||||
style: ListEditorStyle,
|
||||
level: usize,
|
||||
cur_dist: Arc<RwLock<usize>>,
|
||||
}
|
||||
|
||||
impl<ItemEditor, FnMakeItemEditor> TreeNav for ListEditor<ItemEditor, FnMakeItemEditor>
|
||||
|
@ -326,6 +313,7 @@ where ItemEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
|
|||
tree_addr: vec![]
|
||||
}
|
||||
);
|
||||
*self.cur_dist.write().unwrap() += 1;
|
||||
}
|
||||
}
|
||||
TreeNavResult::Continue
|
||||
|
@ -586,7 +574,7 @@ where ItemEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
|
|||
}
|
||||
|
||||
impl<ItemEditor, FnMakeItemEditor> ListEditor<ItemEditor, FnMakeItemEditor>
|
||||
where ItemEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
|
||||
where ItemEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
|
||||
FnMakeItemEditor: Fn() -> Arc<RwLock<ItemEditor>>
|
||||
{
|
||||
pub fn get_seg_seq_view(&self) -> OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>> {
|
||||
|
@ -596,32 +584,39 @@ where ItemEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
|
|||
self.data_port.outer().to_sequence().map(|ed| ed.read().unwrap().get_term_view()),
|
||||
segment_view_port.inner()
|
||||
);
|
||||
|
||||
segment_view_port.into_outer()
|
||||
.map(
|
||||
|segment| match segment {
|
||||
move |segment| {
|
||||
let cursor_col = (90, 60, 200);
|
||||
match segment {
|
||||
ListEditorViewSegment::InsertCursor =>
|
||||
make_label("|")
|
||||
.map_item(
|
||||
|_pt, atom|
|
||||
atom.add_style_back(TerminalStyle::fg_color((90,60,200)))
|
||||
//.add_style_back(TerminalStyle::bg_color((0,0,0)))
|
||||
move |_pt, atom|
|
||||
atom.add_style_back(TerminalStyle::fg_color(cursor_col))
|
||||
.add_style_back(TerminalStyle::bold(true))
|
||||
),
|
||||
ListEditorViewSegment::Select(sub_view) =>
|
||||
sub_view.map_item(
|
||||
|_pt, atom|
|
||||
atom.add_style_front(TerminalStyle::bg_color((90,60,200)))
|
||||
move |_pt, atom| {
|
||||
let old_col = atom.style.bg_color.unwrap_or(cursor_col);
|
||||
atom.add_style_front(TerminalStyle::bg_color(((old_col.0 as f32 * 0.4) as u8, (old_col.1 as f32 * 0.4) as u8, (old_col.2 as f32 * 0.4) as u8)))
|
||||
}
|
||||
),
|
||||
ListEditorViewSegment::Modify(sub_view) => {
|
||||
sub_view.clone().map_item(
|
||||
|_pt, atom|
|
||||
atom.add_style_back(TerminalStyle::bg_color((22,15,50)))
|
||||
move |_pt, atom| {
|
||||
let old_col = atom.style.bg_color.unwrap_or(cursor_col);
|
||||
atom.add_style_front(TerminalStyle::bg_color(((old_col.0 as f32 * 0.4) as u8, (old_col.1 as f32 * 0.4) as u8, (old_col.2 as f32 * 0.4) as u8)))
|
||||
}
|
||||
//.add_style_back(TerminalStyle::bold(true))
|
||||
)
|
||||
},
|
||||
ListEditorViewSegment::View(sub_view) =>
|
||||
sub_view.clone()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -644,6 +639,7 @@ where ItemEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
|
|||
self.get_seg_seq_view()
|
||||
.decorate("\"", "\"", "", 1)
|
||||
.to_grid_horizontal()
|
||||
|
||||
.flatten()
|
||||
}
|
||||
|
||||
|
@ -681,7 +677,8 @@ where ItemEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
|
|||
|
||||
style,
|
||||
make_item_editor,
|
||||
level: 0
|
||||
level: 0,
|
||||
cur_dist: Arc::new(RwLock::new(0))
|
||||
};
|
||||
le.set_style(style);
|
||||
le
|
||||
|
|
|
@ -3,6 +3,7 @@ use {
|
|||
core::{InnerViewPort, Observer, ObserverBroadcast, OuterViewPort, View, ViewPort},
|
||||
projection::ProjectionHelper,
|
||||
sequence::SequenceView,
|
||||
index::{IndexArea},
|
||||
terminal::{make_label, TerminalStyle, TerminalView},
|
||||
},
|
||||
cgmath::Point2,
|
||||
|
@ -141,19 +142,16 @@ pub struct VerticalSexprDecorator {
|
|||
}
|
||||
|
||||
impl View for VerticalSexprDecorator {
|
||||
type Msg = Point2<i16>;
|
||||
type Msg = IndexArea<Point2<i16>>;
|
||||
}
|
||||
|
||||
impl IndexView<Point2<i16>> for VerticalSexprDecorator {
|
||||
type Item = OuterViewPort<dyn TerminalView>;
|
||||
|
||||
fn area(&self) -> Option<Vec<Point2<i16>>> {
|
||||
let mut area = (0..self.items.len()?)
|
||||
.map(|i| Point2::new(1 as i16, i as i16))
|
||||
.collect::<Vec<_>>();
|
||||
area.push(Point2::new(0, 0));
|
||||
area.push(Point2::new(2, self.items.len()? as i16 - 1));
|
||||
Some(area)
|
||||
fn area(&self) -> IndexArea<Point2<i16>> {
|
||||
IndexArea::Range(
|
||||
Point2::new(0, 0) ..= Point2::new(2, std::cmp::max(self.items.len().unwrap() as i16 - 1, 0))
|
||||
)
|
||||
}
|
||||
|
||||
fn get(&self, pt: &Point2<i16>) -> Option<Self::Item> {
|
||||
|
@ -218,9 +216,11 @@ impl VerticalSexprDecorator {
|
|||
opening_port: make_label(opening),
|
||||
closing_port: make_label(closing),
|
||||
items: proj_helper.new_sequence_arg((), items_port, |s: &mut Self, item_idx| {
|
||||
s.cast.notify(&Point2::new(1, *item_idx as i16));
|
||||
s.cast.notify(&Point2::new(2, *item_idx as i16 - 1));
|
||||
s.cast.notify(&Point2::new(2, *item_idx as i16));
|
||||
s.cast.notify(
|
||||
&IndexArea::Range(
|
||||
Point2::new(0, *item_idx as i16) ..= Point2::new(2, *item_idx as i16)
|
||||
)
|
||||
);
|
||||
}),
|
||||
list_style: TerminalStyle::fg_color(match level {
|
||||
0 => (200, 120, 10),
|
||||
|
|
|
@ -16,12 +16,13 @@ use {
|
|||
channel::{
|
||||
ChannelSender, ChannelReceiver,
|
||||
ChannelData,
|
||||
set_channel
|
||||
set_channel,
|
||||
queue_channel
|
||||
}
|
||||
},
|
||||
singleton::{SingletonView},
|
||||
sequence::{SequenceView},
|
||||
index::{IndexView}
|
||||
index::{IndexArea, IndexView}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -75,13 +76,13 @@ where ArgKey: Clone + Hash + Eq,
|
|||
port.get_view_arc()
|
||||
}
|
||||
|
||||
pub fn new_index_arg<Key: Hash + Eq + Clone + Send + Sync + std::fmt::Debug + 'static, Item: 'static>(
|
||||
pub fn new_index_arg<Key: Clone + Send + Sync + 'static, Item: 'static>(
|
||||
&mut self,
|
||||
arg_key: ArgKey,
|
||||
port: OuterViewPort<dyn IndexView<Key, Item = Item>>,
|
||||
notify: impl Fn(&mut P, &Key) + Send + Sync + 'static
|
||||
notify: impl Fn(&mut P, &IndexArea<Key>) + Send + Sync + 'static
|
||||
) -> Arc<RwLock<Option<Arc<dyn IndexView<Key, Item = Item>>>>> {
|
||||
port.add_observer(self.new_arg(arg_key, Arc::new(port.0.clone()), notify, set_channel()));
|
||||
port.add_observer(self.new_arg(arg_key, Arc::new(port.0.clone()), notify, queue_channel()));
|
||||
port.get_view_arc()
|
||||
}
|
||||
|
||||
|
@ -96,7 +97,7 @@ where ArgKey: Clone + Hash + Eq,
|
|||
(tx, rx): (ChannelSender<D>, ChannelReceiver<D>)
|
||||
)
|
||||
-> Arc<RwLock<ProjectionArg<P, V, D>>>
|
||||
where V::Msg: Send + Sync + std::fmt::Debug,
|
||||
where V::Msg: Send + Sync,
|
||||
D::IntoIter: Send + Sync + 'static
|
||||
{
|
||||
self.remove_arg(&arg_key);
|
||||
|
@ -151,8 +152,7 @@ impl<P, V, D> UpdateTask for ProjectionArg<P, V, D>
|
|||
where P: Send + Sync + 'static,
|
||||
V: View + ?Sized,
|
||||
D: ChannelData<Item = V::Msg>,
|
||||
D::IntoIter: Send + Sync,
|
||||
V::Msg: std::fmt::Debug
|
||||
D::IntoIter: Send + Sync
|
||||
{
|
||||
fn update(&self) {
|
||||
if let Some(p) = self.proj.read().unwrap().upgrade() {
|
||||
|
@ -175,8 +175,7 @@ impl<P, V, D> UpdateTask for RwLock<ProjectionArg<P, V, D>>
|
|||
where P: Send + Sync + 'static,
|
||||
V: View + ?Sized,
|
||||
D: ChannelData<Item = V::Msg>,
|
||||
D::IntoIter: Send + Sync,
|
||||
V::Msg: std::fmt::Debug
|
||||
D::IntoIter: Send + Sync
|
||||
{
|
||||
fn update(&self) {
|
||||
self.read().unwrap().update();
|
||||
|
@ -225,19 +224,18 @@ where P: Send + Sync + 'static,
|
|||
impl<P, Key, Item, D> Observer<dyn IndexView<Key, Item = Item>> for ProjectionArg<P, dyn IndexView<Key, Item = Item>, D>
|
||||
where P: Send + Sync + 'static,
|
||||
Key: Clone + Send + Sync,
|
||||
D: ChannelData<Item = Key>,
|
||||
D: ChannelData<Item = IndexArea<Key>>,
|
||||
D::IntoIter: Send + Sync
|
||||
{
|
||||
fn reset(&mut self, new_src: Option<Arc<dyn IndexView<Key, Item = Item>>>) {
|
||||
let old_area = self.src.area();
|
||||
self.src = new_src;
|
||||
let new_area = self.src.area();
|
||||
|
||||
if let Some(area) = old_area { self.notify_each(area); }
|
||||
if let Some(area) = new_area { self.notify_each(area); }
|
||||
self.notify(&old_area);
|
||||
self.notify(&self.src.area())
|
||||
}
|
||||
|
||||
fn notify(&mut self, msg: &Key) {
|
||||
fn notify(&mut self, msg: &IndexArea<Key>) {
|
||||
self.tx.send(msg.clone());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@ use {
|
|||
std::sync::RwLock,
|
||||
crate::{
|
||||
core::{
|
||||
View, Observer, ObserverExt, ObserverBroadcast,
|
||||
View, Observer, ObserverBroadcast,
|
||||
ViewPort, InnerViewPort, OuterViewPort
|
||||
},
|
||||
sequence::SequenceView,
|
||||
index::IndexView,
|
||||
index::{IndexArea, IndexView},
|
||||
grid::GridView
|
||||
}
|
||||
};
|
||||
|
@ -56,7 +56,7 @@ impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
|
|||
|
||||
impl<SrcView> View for Sequence2Index<SrcView>
|
||||
where SrcView: SequenceView + ?Sized + 'static {
|
||||
type Msg = usize;
|
||||
type Msg = IndexArea<usize>;
|
||||
}
|
||||
|
||||
impl<SrcView> IndexView<usize> for Sequence2Index<SrcView>
|
||||
|
@ -67,8 +67,16 @@ where SrcView: SequenceView + ?Sized + 'static {
|
|||
self.src_view.get(key)
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<usize>> {
|
||||
Some((0 .. self.src_view.len()?).collect())
|
||||
fn area(&self) -> IndexArea<usize> {
|
||||
if let Some(len) = self.src_view.len() {
|
||||
if len > 0 {
|
||||
IndexArea::Range(0 ..= len-1)
|
||||
} else {
|
||||
IndexArea::Empty
|
||||
}
|
||||
} else {
|
||||
IndexArea::Full
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,14 +85,13 @@ where SrcView: SequenceView + ?Sized + 'static {
|
|||
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||
let old_area = self.area();
|
||||
self.src_view = view;
|
||||
let new_area = self.area();
|
||||
|
||||
if let Some(area) = old_area { self.cast.notify_each(area); }
|
||||
if let Some(area) = new_area { self.cast.notify_each(area); }
|
||||
self.cast.notify(&old_area);
|
||||
self.cast.notify(&self.area());
|
||||
}
|
||||
|
||||
fn notify(&mut self, msg: &usize) {
|
||||
self.cast.notify(msg);
|
||||
fn notify(&mut self, idx: &usize) {
|
||||
self.cast.notify(&IndexArea::Set(vec![ *idx ]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use {
|
|||
crate::{
|
||||
singleton::{SingletonView},
|
||||
core::{
|
||||
Observer, ObserverExt, ObserverBroadcast,
|
||||
Observer, ObserverBroadcast,
|
||||
View, ViewPort, OuterViewPort
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ use {
|
|||
std::sync::RwLock,
|
||||
crate::{
|
||||
singleton::{SingletonView},
|
||||
index::{IndexView},
|
||||
index::{IndexArea, IndexView},
|
||||
grid::{GridView},
|
||||
core::{
|
||||
Observer, ObserverExt, ObserverBroadcast,
|
||||
Observer, ObserverBroadcast,
|
||||
View, ViewPort, OuterViewPort
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ where SrcView: SingletonView + ?Sized
|
|||
impl<SrcView> View for Singleton2Index<SrcView>
|
||||
where SrcView: SingletonView + ?Sized
|
||||
{
|
||||
type Msg = ();
|
||||
type Msg = IndexArea<()>;
|
||||
}
|
||||
|
||||
impl<SrcView> IndexView<()> for Singleton2Index<SrcView>
|
||||
|
@ -60,9 +60,10 @@ where SrcView: SingletonView + ?Sized
|
|||
{
|
||||
type Item = SrcView::Item;
|
||||
|
||||
fn area(&self) -> Option<Vec<()>> {
|
||||
Some(vec![()])
|
||||
fn area(&self) -> IndexArea<()> {
|
||||
IndexArea::Full
|
||||
}
|
||||
|
||||
fn get(&self, _msg: &()) -> Option<Self::Item> {
|
||||
Some(self.src_view.as_ref().unwrap().get())
|
||||
}
|
||||
|
@ -75,11 +76,11 @@ where SrcView: SingletonView + ?Sized
|
|||
{
|
||||
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||
self.src_view = view;
|
||||
self.cast.notify(&());
|
||||
self.cast.notify(&IndexArea::Full);
|
||||
}
|
||||
|
||||
fn notify(&mut self, msg: &()) {
|
||||
self.cast.notify(msg);
|
||||
fn notify(&mut self, _: &()) {
|
||||
self.cast.notify(&IndexArea::Full);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ use {
|
|||
core::{ViewPort, OuterViewPort},
|
||||
singleton::{SingletonView, SingletonBuffer},
|
||||
sequence::{SequenceView},
|
||||
vec::VecBuffer,
|
||||
terminal::{TerminalView, TerminalStyle, TerminalEvent, TerminalEditor, TerminalEditorResult},
|
||||
list::{ListEditor, sexpr::ListDecoration},
|
||||
tree_nav::{TreeNav, TreeNavResult, TreeCursor}
|
||||
|
|
|
@ -5,8 +5,8 @@ use {
|
|||
std::sync::RwLock,
|
||||
cgmath::Point2,
|
||||
crate::{
|
||||
core::{InnerViewPort, OuterViewPort, Observer, ObserverBroadcast},
|
||||
index::{ImplIndexView},
|
||||
core::{InnerViewPort, OuterViewPort, View, Observer, ObserverBroadcast},
|
||||
index::{IndexArea, IndexView},
|
||||
terminal::{TerminalAtom, TerminalView},
|
||||
projection::ProjectionHelper
|
||||
}
|
||||
|
@ -46,8 +46,8 @@ impl TerminalCompositor {
|
|||
self.proj_helper.new_index_arg(
|
||||
idx,
|
||||
v,
|
||||
|s: &mut Self, pos| {
|
||||
s.cast.notify(pos);
|
||||
|s: &mut Self, area| {
|
||||
s.cast.notify(area);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -56,9 +56,12 @@ impl TerminalCompositor {
|
|||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl ImplIndexView for TerminalCompositor {
|
||||
type Key = Point2<i16>;
|
||||
type Value = TerminalAtom;
|
||||
impl View for TerminalCompositor {
|
||||
type Msg = IndexArea<Point2<i16>>;
|
||||
}
|
||||
|
||||
impl IndexView<Point2<i16>> for TerminalCompositor {
|
||||
type Item = TerminalAtom;
|
||||
|
||||
fn get(&self, pos: &Point2<i16>) -> Option<TerminalAtom> {
|
||||
let mut atom = None;
|
||||
|
@ -74,21 +77,11 @@ impl ImplIndexView for TerminalCompositor {
|
|||
atom
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Point2<i16>>> {
|
||||
let mut area = Some(Vec::new());
|
||||
fn area(&self) -> IndexArea<Point2<i16>> {
|
||||
let mut area = IndexArea::Empty;
|
||||
|
||||
for layer in self.layers.iter() {
|
||||
if let (
|
||||
Some(mut new_area),
|
||||
Some(area)
|
||||
) = (
|
||||
layer.area(),
|
||||
area.as_mut()
|
||||
) {
|
||||
area.append(&mut new_area);
|
||||
} else {
|
||||
area = None;
|
||||
}
|
||||
area = area.union(layer.area());
|
||||
}
|
||||
|
||||
area
|
||||
|
|
|
@ -27,6 +27,7 @@ use {
|
|||
set_channel
|
||||
}
|
||||
},
|
||||
index::{IndexArea},
|
||||
grid::{GridWindowIterator}
|
||||
},
|
||||
super::{
|
||||
|
@ -119,24 +120,40 @@ struct TermOutObserver {
|
|||
writer: Arc<TermOutWriter>
|
||||
}
|
||||
|
||||
impl Observer<dyn TerminalView> for TermOutObserver {
|
||||
fn reset(&mut self, view: Option<Arc<dyn TerminalView>>) {
|
||||
self.writer.reset();
|
||||
|
||||
let (w, h) = termion::terminal_size().unwrap();
|
||||
if let Some(view) = view {
|
||||
for pos in view.area().unwrap_or(
|
||||
GridWindowIterator::from(
|
||||
Point2::new(0, 0) .. Point2::new(w as i16, h as i16)
|
||||
).collect()
|
||||
) {
|
||||
self.dirty_pos_tx.send(pos);
|
||||
impl TermOutObserver {
|
||||
fn send_area(&mut self, area: IndexArea<Point2<i16>>) {
|
||||
match area {
|
||||
IndexArea::Empty => {},
|
||||
IndexArea::Full => {
|
||||
let (w, h) = termion::terminal_size().unwrap();
|
||||
for pos in GridWindowIterator::from(Point2::new(0, 0) .. Point2::new(w as i16, h as i16)) {
|
||||
self.dirty_pos_tx.send(pos);
|
||||
}
|
||||
}
|
||||
IndexArea::Range(r) => {
|
||||
for pos in GridWindowIterator::from(r) {
|
||||
self.dirty_pos_tx.send(pos);
|
||||
}
|
||||
}
|
||||
IndexArea::Set(v) => {
|
||||
for pos in v {
|
||||
self.dirty_pos_tx.send(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn notify(&mut self, pos: &Point2<i16>) {
|
||||
self.dirty_pos_tx.send(*pos);
|
||||
impl Observer<dyn TerminalView> for TermOutObserver {
|
||||
fn reset(&mut self, view: Option<Arc<dyn TerminalView>>) {
|
||||
self.writer.reset();
|
||||
if let Some(view) = view {
|
||||
self.send_area(view.area());
|
||||
}
|
||||
}
|
||||
|
||||
fn notify(&mut self, area: &IndexArea<Point2<i16>>) {
|
||||
self.send_area(area.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,10 +183,24 @@ impl TermOutWriter {
|
|||
|
||||
// draw atoms until view port is destroyed
|
||||
while let Some(dirty_pos) = self.dirty_pos_rx.recv().await {
|
||||
let (w, h) = termion::terminal_size().unwrap();
|
||||
|
||||
if let Some(view) = self.view.read().unwrap().as_ref() {
|
||||
let mut out = self.out.write().unwrap();
|
||||
|
||||
for pos in dirty_pos.into_iter().filter(|p| p.x >= 0 && p.y >= 0) {
|
||||
let d = dirty_pos.into_iter().filter(|p| p.x >= 0 && p.y >= 0 && p.x < w as i16 && p.y < w as i16);//.collect::<Vec<_>>();
|
||||
/*
|
||||
d.sort_by(|a,b| {
|
||||
if a.y < b.y {
|
||||
std::cmp::Ordering::Less
|
||||
} else if a.y == b.y {
|
||||
a.x.cmp(&b.x)
|
||||
} else {
|
||||
std::cmp::Ordering::Greater
|
||||
}
|
||||
});
|
||||
*/
|
||||
for pos in d {
|
||||
if pos != cur_pos {
|
||||
write!(out, "{}", termion::cursor::Goto(pos.x as u16 + 1, pos.y as u16 + 1))?;
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
# to the user in the error, instead of "error: invalid channel name '[toolchain]'".
|
||||
|
||||
[toolchain]
|
||||
channel = "nightly-2021-09-29"
|
||||
channel = "nightly-2021-10-26"
|
||||
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
|
||||
|
|
|
@ -13,7 +13,7 @@ use{
|
|||
ObserverBroadcast,
|
||||
context::{ReprTree, Object, MorphismType, MorphismMode, Context},
|
||||
port::{UpdateTask}},
|
||||
index::{IndexView},
|
||||
index::{IndexArea, IndexView},
|
||||
grid::{GridWindowIterator},
|
||||
terminal::{
|
||||
Terminal,
|
||||
|
@ -57,14 +57,19 @@ impl AsciiBox {
|
|||
if self.extent != new_extent {
|
||||
let old_extent = self.extent;
|
||||
self.extent = new_extent;
|
||||
self.notify_each(GridWindowIterator::from(Point2::new(0, 0) .. Point2::new(2+std::cmp::max(old_extent.x, new_extent.x), 2+std::cmp::max(old_extent.y, new_extent.y))));
|
||||
self.cast.notify(
|
||||
&IndexArea::Range(
|
||||
Point2::new(0, 0) ..=
|
||||
Point2::new(
|
||||
1+std::cmp::max(old_extent.x, new_extent.x),
|
||||
1+std::cmp::max(old_extent.y, new_extent.y))));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fit_content(&mut self) {
|
||||
if let Some(c) = self.content.as_ref() {
|
||||
let p = c.range().end;
|
||||
self.resize(Vector2::new(p.x, p.y));
|
||||
let p = *c.area().range().end();
|
||||
self.resize(Vector2::new(p.x+1, p.y+1));
|
||||
} else {
|
||||
self.resize(Vector2::new(0, 0));
|
||||
}
|
||||
|
@ -74,17 +79,17 @@ impl AsciiBox {
|
|||
impl Observer<dyn TerminalView> for AsciiBox {
|
||||
fn reset(&mut self, new_content: Option<Arc<dyn TerminalView>>) {
|
||||
self.content = new_content;
|
||||
self.notify_each(GridWindowIterator::from(Point2::new(0, 0) .. Point2::new(self.extent.x+2, self.extent.y+2)));
|
||||
self.fit_content();
|
||||
}
|
||||
|
||||
fn notify(&mut self, pt: &Point2<i16>) {
|
||||
self.cast.notify(&(pt + Vector2::new(1, 1)));
|
||||
fn notify(&mut self, area: &IndexArea<Point2<i16>>) {
|
||||
self.cast.notify(&area.map(|pt| pt + Vector2::new(1, 1)));
|
||||
self.fit_content();
|
||||
}
|
||||
}
|
||||
|
||||
impl View for AsciiBox {
|
||||
type Msg = Point2<i16>;
|
||||
type Msg = IndexArea<Point2<i16>>;
|
||||
}
|
||||
|
||||
impl IndexView<Point2<i16>> for AsciiBox {
|
||||
|
@ -114,19 +119,19 @@ impl IndexView<Point2<i16>> for AsciiBox {
|
|||
None
|
||||
}
|
||||
} else if
|
||||
pt.x > 0 &&
|
||||
pt.y > 0 &&
|
||||
pt.x < self.extent.x+1 &&
|
||||
pt.y < self.extent.y+1
|
||||
{
|
||||
self.content.get(&(pt - Vector2::new(1, 1)))
|
||||
Some(self.content.get(&(pt - Vector2::new(1, 1))).unwrap_or(TerminalAtom::from(' ')))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Point2<i16>>> {
|
||||
Some(GridWindowIterator::from(
|
||||
Point2::new(0, 0) .. Point2::new(self.extent.x+2, self.extent.y+2)
|
||||
).collect())
|
||||
fn area(&self) -> IndexArea<Point2<i16>> {
|
||||
IndexArea::Range(Point2::new(0, 0) ..= Point2::new(1,1)+self.extent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
131
shell/src/box.rs
131
shell/src/box.rs
|
@ -1,131 +0,0 @@
|
|||
|
||||
use{
|
||||
std::sync::{Arc, RwLock},
|
||||
cgmath::{Point2, Vector2},
|
||||
nested::{
|
||||
core::{
|
||||
View,
|
||||
ViewPort,
|
||||
InnerViewPort,
|
||||
OuterViewPort,
|
||||
Observer,
|
||||
ObserverExt,
|
||||
ObserverBroadcast,
|
||||
context::{ReprTree, Object, MorphismType, MorphismMode, Context},
|
||||
port::{UpdateTask}},
|
||||
index::{IndexView},
|
||||
grid::{GridWindowIterator},
|
||||
terminal::{
|
||||
Terminal,
|
||||
TerminalStyle,
|
||||
TerminalAtom,
|
||||
TerminalCompositor,
|
||||
TerminalEvent,
|
||||
make_label,
|
||||
TerminalView,
|
||||
TerminalEditor},
|
||||
}
|
||||
};
|
||||
|
||||
pub struct AsciiBox {
|
||||
content: Option<Arc<dyn TerminalView>>,
|
||||
extent: Vector2<i16>,
|
||||
|
||||
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>
|
||||
}
|
||||
|
||||
impl AsciiBox {
|
||||
pub fn new(
|
||||
extent: Vector2<i16>,
|
||||
content_port: OuterViewPort<dyn TerminalView>,
|
||||
output_port: InnerViewPort<dyn TerminalView>
|
||||
) -> Arc<RwLock<Self>> {
|
||||
let ascii_box = Arc::new(RwLock::new(AsciiBox {
|
||||
content: None,
|
||||
extent,
|
||||
cast: output_port.get_broadcast()
|
||||
}));
|
||||
|
||||
output_port.0.update_hooks.write().unwrap().push(Arc::new(content_port.0.clone()));
|
||||
output_port.set_view(Some(ascii_box.clone()));
|
||||
content_port.add_observer(ascii_box.clone());
|
||||
|
||||
ascii_box
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, new_extent: Vector2<i16>) {
|
||||
if self.extent != new_extent {
|
||||
let old_extent = self.extent;
|
||||
self.extent = new_extent;
|
||||
self.notify_each(GridWindowIterator::from(Point2::new(0, 0) .. Point2::new(2+std::cmp::max(old_extent.x, new_extent.x), 2+std::cmp::max(old_extent.y, new_extent.y))));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fit_content(&mut self) {
|
||||
if let Some(c) = self.content.as_ref() {
|
||||
let p = c.range().end;
|
||||
self.resize(Vector2::new(p.x, p.y));
|
||||
} else {
|
||||
self.resize(Vector2::new(0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<dyn TerminalView> for AsciiBox {
|
||||
fn reset(&mut self, new_content: Option<Arc<dyn TerminalView>>) {
|
||||
self.content = new_content;
|
||||
self.notify_each(GridWindowIterator::from(Point2::new(0, 0) .. Point2::new(self.extent.x+2, self.extent.y+2)));
|
||||
}
|
||||
|
||||
fn notify(&mut self, pt: &Point2<i16>) {
|
||||
self.cast.notify(&(pt + Vector2::new(1, 1)));
|
||||
}
|
||||
}
|
||||
|
||||
impl View for AsciiBox {
|
||||
type Msg = Point2<i16>;
|
||||
}
|
||||
|
||||
impl IndexView<Point2<i16>> for AsciiBox {
|
||||
type Item = TerminalAtom;
|
||||
|
||||
fn get(&self, pt: &Point2<i16>) -> Option<TerminalAtom> {
|
||||
if pt.x == 0 || pt.x == self.extent.x+1 {
|
||||
// vertical line
|
||||
if pt.y == 0 && pt.x == 0 {
|
||||
Some(TerminalAtom::from('╭'))
|
||||
} else if pt.y == 0 && pt.x == self.extent.x+1 {
|
||||
Some(TerminalAtom::from('╮'))
|
||||
} else if pt.y > 0 && pt.y < self.extent.y+1 {
|
||||
Some(TerminalAtom::from('│'))
|
||||
} else if pt.y == self.extent.y+1 && pt.x == 0 {
|
||||
Some(TerminalAtom::from('╰'))
|
||||
} else if pt.y == self.extent.y+1 && pt.x == self.extent.x+1 {
|
||||
Some(TerminalAtom::from('╯'))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if pt.y == 0 || pt.y == self.extent.y+1 {
|
||||
// horizontal line
|
||||
if pt.x > 0 && pt.x < self.extent.x+1 {
|
||||
Some(TerminalAtom::from('─'))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if
|
||||
pt.x < self.extent.x+1 &&
|
||||
pt.y < self.extent.y+1
|
||||
{
|
||||
self.content.get(&(pt - Vector2::new(1, 1)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn area(&self) -> Option<Vec<Point2<i16>>> {
|
||||
Some(GridWindowIterator::from(
|
||||
Point2::new(0, 0) .. Point2::new(self.extent.x+2, self.extent.y+2)
|
||||
).collect())
|
||||
}
|
||||
|
||||
}
|
|
@ -21,7 +21,7 @@ use{
|
|||
ObserverBroadcast,
|
||||
context::{ReprTree, Object, MorphismType, MorphismMode, Context},
|
||||
port::{UpdateTask}},
|
||||
index::{IndexView},
|
||||
index::{IndexView, IndexArea},
|
||||
grid::{GridWindowIterator},
|
||||
sequence::{SequenceView, SequenceViewExt},
|
||||
vec::{VecBuffer},
|
||||
|
@ -44,6 +44,24 @@ use{
|
|||
}
|
||||
};
|
||||
|
||||
struct TestView {}
|
||||
|
||||
impl View for TestView {
|
||||
type Msg = IndexArea<Point2<i16>>;
|
||||
}
|
||||
|
||||
impl IndexView<Point2<i16>> for TestView {
|
||||
type Item = TerminalAtom;
|
||||
|
||||
fn get(&self, pt: &Point2<i16>) -> Option<TerminalAtom> {
|
||||
Some(TerminalAtom::from('.'))
|
||||
}
|
||||
|
||||
fn area(&self) -> IndexArea<Point2<i16>> {
|
||||
IndexArea::Full
|
||||
}
|
||||
}
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
let term_port = ViewPort::new();
|
||||
|
@ -99,35 +117,22 @@ async fn main() {
|
|||
tree_addr: vec![ 0 ]
|
||||
});
|
||||
|
||||
loop {
|
||||
term_port.update();
|
||||
/*
|
||||
if let Some(p) = pty.as_mut() {
|
||||
if p.get_status() {
|
||||
if let Some(ptybox) = ptybox.take() {
|
||||
ptybox.write().unwrap().fit_content();
|
||||
}
|
||||
pty = None;
|
||||
process_list_editor.up();
|
||||
let tp = term_port.clone();
|
||||
async_std::task::spawn(
|
||||
async move {
|
||||
loop {
|
||||
tp.update();
|
||||
async_std::task::sleep(std::time::Duration::from_millis(10)).await;
|
||||
}
|
||||
}
|
||||
*/
|
||||
term_port.update();
|
||||
);
|
||||
|
||||
loop {
|
||||
let ev = term.next_event().await;
|
||||
/*
|
||||
if let Some(pty) = pty.as_mut() {
|
||||
pty.handle_terminal_event(&ev);
|
||||
} else {
|
||||
*/
|
||||
match ev {
|
||||
TerminalEvent::Resize(new_size) => {
|
||||
cur_size.set(new_size);
|
||||
term_port.inner().get_broadcast().notify_each(
|
||||
nested::grid::GridWindowIterator::from(
|
||||
Point2::new(0,0) .. Point2::new(new_size.x, new_size.y)
|
||||
)
|
||||
);
|
||||
term_port.inner().get_broadcast().notify(&IndexArea::Full);
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::Ctrl('c'))) |
|
||||
TerminalEvent::Input(Event::Key(Key::Ctrl('g'))) |
|
||||
|
@ -157,27 +162,7 @@ async fn main() {
|
|||
process_list_editor.goto_end();
|
||||
}
|
||||
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
|
||||
//let mut output_port = ViewPort::new();
|
||||
process_list_editor.get_item().unwrap().write().unwrap().launch_pty2();
|
||||
/*
|
||||
let box_port = ViewPort::new();
|
||||
let test_box = AsciiBox::new(
|
||||
Vector2::new(80, 25),
|
||||
output_port.outer()
|
||||
.map_item(|_,a| a.add_style_back(TerminalStyle::fg_color((230, 230, 230)))),
|
||||
box_port.inner()
|
||||
);
|
||||
|
||||
ptybox = Some(test_box.clone());
|
||||
|
||||
table_buf.remove(Point2::new(0, y-1));
|
||||
|
||||
let mut p = box_port.outer().map_item(|_idx, x| x.add_style_back(TerminalStyle::fg_color((90, 120, 100))) .offset(Vector2::new(0, -1));
|
||||
table_port.update_hooks.write().unwrap().push(Arc::new(p.clone().0));
|
||||
|
||||
y += 1;
|
||||
table_buf.insert(Point2::new(0, y), p.clone());
|
||||
*/
|
||||
}
|
||||
|
||||
ev => {
|
||||
|
@ -229,7 +214,8 @@ async fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
//drop(term);
|
||||
drop(term);
|
||||
drop(term_port);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -134,10 +134,6 @@ impl ProcessLauncher {
|
|||
|
||||
pub fn launch_pty2(&mut self) {
|
||||
self.launch_pty(self.pty_port.inner());
|
||||
//self.ptybox.write().unwrap().fit_content();
|
||||
|
||||
//let mut p =
|
||||
//table_port.update_hooks.write().unwrap().push(Arc::new(p.clone().0));
|
||||
}
|
||||
|
||||
pub fn launch_pty(&mut self, port: InnerViewPort<dyn TerminalView>) -> Option<crate::pty::PTY> {
|
||||
|
|
Loading…
Reference in a new issue