Attempt 2 to make mutability-independent UI

This commit is contained in:
Andrew Golovashevich 2026-02-21 02:59:46 +03:00
parent f295dc7a12
commit 09b732cfbc
11 changed files with 436 additions and 311 deletions

View File

@ -1,7 +1,4 @@
use super::{
_ConstMutSwitchUiCallback, _ConstMutSwitchUiTableBaseCallback,
_ConstMutSwitchUiTableRowCallback, ConstMutSwitchUi, ConstRef, RefType,
};
use super::{_ConstMutSwitchUiCallback, ConstMutSwitchUi, ConstRef, RefType, _ConstMutSwitchUiTableHeaderCallback};
use eframe::egui::{ScrollArea, Ui};
use eframe::emath::Numeric;
use std::ops::RangeInclusive;
@ -52,9 +49,8 @@ impl ConstMutSwitchUi for ConstUI<'_> {
self.ui.label("");
}
fn horizontal<F: _ConstMutSwitchUiCallback<ConstRef>>(&mut self, clojure: &mut F::Clojure<'_>) {
self.ui
.horizontal(|ui| F::render(&mut ConstUI { ui }, clojure));
fn horizontal(&mut self, scope: impl _ConstMutSwitchUiCallback<ConstRef>) {
self.ui.horizontal(|ui| scope.render(&mut ConstUI { ui }));
}
fn button<T>(&mut self, label: &str, _storage: &mut &T, _on_click: impl FnOnce(&mut T)) {
@ -65,33 +61,22 @@ impl ConstMutSwitchUi for ConstUI<'_> {
self.ui.separator();
}
fn scroll_area_2<F: _ConstMutSwitchUiCallback<Self::RefType>>(
fn scroll_area_2(
&mut self,
clojure: &mut F::Clojure<'_>,
scope: impl _ConstMutSwitchUiCallback<Self::RefType>,
) {
ScrollArea::both()
.auto_shrink([false, false])
.show_viewport(self.ui, |ui, _| F::render(&mut ConstUI { ui }, clojure));
.show_viewport(self.ui, |ui, _|scope.render(&mut ConstUI { ui }));
}
fn table<
'a,
F: _ConstMutSwitchUiTableBaseCallback<ConstRef, Body = _B>,
_B: _ConstMutSwitchUiTableRowCallback<ConstRef, Clojure<'a> = _BC>,
_BC,
>(
fn table(
&mut self,
vscroll: bool,
columns: usize,
header_clojure: <F::Header as _ConstMutSwitchUiTableRowCallback<ConstRef>>::Clojure<'_>,
body_data: impl Iterator<Item = _BC>,
header: impl _ConstMutSwitchUiTableHeaderCallback<Self::RefType>,
) {
super::render_table::<ConstUI, F, Self>(
self.ui,
vscroll,
columns,
header_clojure,
body_data,
);
super::render_table::<ConstRef, Self>(self.ui, vscroll, columns, header);
}
}

View File

@ -6,9 +6,9 @@ mod r#trait;
pub use r#const::ConstUI;
pub use r#mut::MutUI;
pub use r#ref::{ConstRef, MutRef, RefGetter, RefType, _IteratorGetter};
pub use r#ref::{ConstRef, MutRef, RefType};
use table::render_table;
pub use table::{
_ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUiTableRow,
_ConstMutSwitchUiTableHeaderCallback, _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUiTableRow,
};
pub use r#trait::{_ConstMutSwitchUiCallback, ConstMutSwitchUi};

View File

@ -1,4 +1,4 @@
use super::{ConstMutSwitchUi, MutRef, RefType, _ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback};
use super::{_ConstMutSwitchUiTableHeaderCallback, ConstMutSwitchUi, MutRef, RefType};
use crate::gui::const_mut_switch::r#trait::_ConstMutSwitchUiCallback;
use eframe::egui::{ScrollArea, Ui};
use eframe::emath::Numeric;
@ -8,6 +8,12 @@ pub struct MutUI<'ui> {
ui: &'ui mut Ui,
}
impl<'ui> MutUI<'ui> {
pub fn wrap(ui: &'ui mut Ui) -> Self {
return Self { ui };
}
}
impl ConstMutSwitchUi for MutUI<'_> {
type RefType = MutRef;
@ -15,7 +21,12 @@ impl ConstMutSwitchUi for MutUI<'_> {
self.ui.label(s);
}
fn slider<T: Numeric>(&mut self, range: RangeInclusive<T>, step: f64, storage: &mut <Self::RefType as RefType>::Ref<'_, T>) {
fn slider<T: Numeric>(
&mut self,
range: RangeInclusive<T>,
step: f64,
storage: &mut <Self::RefType as RefType>::Ref<'_, T>,
) {
crate::gui::slider(self.ui, *storage, range, step);
}
@ -33,9 +44,8 @@ impl ConstMutSwitchUi for MutUI<'_> {
self.ui.label("");
}
fn horizontal<I: _ConstMutSwitchUiCallback<MutRef>>(&mut self, clojure: &mut I::Clojure<'_>) {
self.ui
.horizontal(|ui| I::render(&mut MutUI { ui }, clojure));
fn horizontal(&mut self, scope: impl _ConstMutSwitchUiCallback<MutRef>) {
self.ui.horizontal(|ui| scope.render(&mut MutUI { ui }));
}
fn button<T>(&mut self, label: &str, storage: &mut &mut T, on_click: impl FnOnce(&mut T)) {
@ -48,27 +58,18 @@ impl ConstMutSwitchUi for MutUI<'_> {
self.ui.separator();
}
fn scroll_area_2<F: _ConstMutSwitchUiCallback<Self::RefType>>(
&mut self,
clojure: &mut F::Clojure<'_>,
) {
fn scroll_area_2(&mut self, scope: impl _ConstMutSwitchUiCallback<MutRef>) {
ScrollArea::both()
.auto_shrink([false, false])
.show_viewport(self.ui, |ui, _| F::render(&mut MutUI { ui }, clojure));
.show_viewport(self.ui, |ui, _| scope.render(&mut MutUI { ui }));
}
fn table<
'a,
F: _ConstMutSwitchUiTableBaseCallback<MutRef, Body = _B>,
_B: _ConstMutSwitchUiTableRowCallback<MutRef, Clojure<'a> = _BC>,
_BC,
>(
fn table(
&mut self,
vscroll: bool,
columns: usize,
header_clojure: <F::Header as _ConstMutSwitchUiTableRowCallback<MutRef>>::Clojure<'_>,
body_data: impl Iterator<Item = _BC>,
header: impl _ConstMutSwitchUiTableHeaderCallback<Self::RefType>,
) {
super::render_table::<MutUI, F, Self>(self.ui, vscroll, columns, header_clojure, body_data);
super::render_table::<MutRef, Self>(self.ui, vscroll, columns, header);
}
}

View File

@ -1,9 +1,8 @@
use crate::gui::const_mut_switch::ConstMutSwitchUi;
use std::ops::Deref;
use std::ptr::NonNull;
pub trait RefType {
type Ref<'a, T: 'a>: Deref<Target = T>;
type Ref<'a, T: 'a>: Deref<Target=T>;
fn _get<'r, 'f: 'r, 's: 'f, S, M>(
storge: &'f mut Self::Ref<'s, S>,
@ -13,32 +12,6 @@ pub trait RefType {
unsafe fn _to_ptr<T>(r: Self::Ref<'_, T>) -> NonNull<T>;
unsafe fn _from_ptr<'a, T>(r: NonNull<T>) -> Self::Ref<'a, T>;
fn _iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>(
storge: &'f mut Self::Ref<'s, F::C>,
) -> impl Iterator<Item = Self::Ref<'r, F::T<'r>>>;
fn _enum_iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>(
storge: &'f mut Self::Ref<'s, F::C>,
) -> impl Iterator<Item = Self::Ref<'r, F::T<'r>>>;
}
pub trait _IteratorGetter {
type C;
type T<'a>: 'a;
fn get_const_iterator<'a>(cont: &'a Self::C) -> impl Iterator<Item = &Self::T<'a>>;
fn get_mut_iterator<'a>(cont: &'a mut Self::C) -> impl Iterator<Item = &mut Self::T<'a>>;
}
pub trait _EnumeratedIteratorGetter {
type C;
type T<'a>: 'a;
fn get_const_iterator<'a>(cont: &'a Self::C) -> impl Iterator<Item = (usize, &Self::T<'a>)>;
fn get_mut_iterator<'a>(
cont: &'a mut Self::C,
) -> impl Iterator<Item = (usize, &mut Self::T<'a>)>;
}
pub struct ConstRef {}
@ -61,12 +34,6 @@ impl RefType for ConstRef {
unsafe fn _from_ptr<'a, T>(r: NonNull<T>) -> Self::Ref<'a, T> {
return r.as_ref();
}
fn _iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>(
storge: &'f mut Self::Ref<'s, F::C>,
) -> impl Iterator<Item = &'r F::T<'r>> {
return F::get_const_iterator(storge);
}
}
pub struct MutRef {}
@ -89,18 +56,4 @@ impl RefType for MutRef {
unsafe fn _from_ptr<'a, T>(mut r: NonNull<T>) -> Self::Ref<'a, T> {
return r.as_mut();
}
fn _iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>(
storge: &'f mut Self::Ref<'s, F::C>,
) -> impl Iterator<Item = &'r mut F::T<'r>> {
return F::get_mut_iterator(storge);
}
}
pub trait RefGetter {
type Ref<'a, T: 'a>;
}
impl<C: ConstMutSwitchUi> RefGetter for C {
type Ref<'a, T: 'a> = <C::RefType as RefType>::Ref<'a, T>;
}

View File

@ -1,30 +1,32 @@
use super::{_ConstMutSwitchUiCallback, ConstMutSwitchUi, ConstRef, ConstUI, MutRef, MutUI, RefType};
#![allow(deprecated)]
use super::{
_ConstMutSwitchUiCallback, ConstMutSwitchUi, ConstRef, ConstUI, MutRef, MutUI, RefType,
};
use crate::LambdaIterator;
use eframe::egui::Ui;
use egui_extras::{Column, TableBuilder, TableRow};
use std::marker::PhantomData;
use std::mem::uninitialized;
pub trait _ConstMutSwitchUiTableBaseCallback<RefType: super::RefType> {
type Header: _ConstMutSwitchUiTableRowCallback<RefType>;
type Body: _ConstMutSwitchUiTableRowCallback<RefType>;
pub trait _ConstMutSwitchUiTableHeaderCallback<RefType: super::RefType> {
type RowRender: _ConstMutSwitchUiTableRowCallback<RefType>;
type RowsIterator: LambdaIterator<Item = Self::RowRender>;
fn header_to_body<'a>(
header: &mut <Self::Header as _ConstMutSwitchUiTableRowCallback<RefType>>::Clojure<'a>,
) -> impl Iterator<Item = <Self::Body as _ConstMutSwitchUiTableRowCallback<RefType>>::Clojure<'a>>;
fn render_header(
self,
ctx: &mut impl ConstMutSwitchUiTableRow<RefType = RefType>,
) -> Self::RowsIterator;
}
pub trait _ConstMutSwitchUiTableRowCallback<RefType: super::RefType> {
type Clojure<'a>;
fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RefType>>(
row: &mut Row,
clojure: &mut Self::Clojure<'_>,
);
fn render_row(self, row: &mut impl ConstMutSwitchUiTableRow<RefType = RefType>);
}
pub trait ConstMutSwitchUiTableRow {
type RefType: RefType;
fn cell<F: _ConstMutSwitchUiCallback<Self::RefType>>(&mut self, clojure: &mut F::Clojure<'_>);
fn cell(&mut self, scope: impl _ConstMutSwitchUiCallback<Self::RefType>);
}
pub(super) struct ConstMutSwitchUiTableRowImpl<
@ -42,13 +44,12 @@ impl<RefType: super::RefType, Constructor: __SwitchUiConstructor1<RefType>> Cons
{
type RefType = RefType;
fn cell<F: _ConstMutSwitchUiCallback<Self::RefType>>(&mut self, clojure: &mut F::Clojure<'_>) {
self.row
.col(|ui| F::render(&mut Constructor::__wrap(ui), clojure));
fn cell(&mut self, cell: impl _ConstMutSwitchUiCallback<Self::RefType>) {
self.row.col(|ui| cell.render(&mut Constructor::__wrap(ui)));
}
}
trait __SwitchUiConstructor1<RefType: super::RefType> {
pub(super) trait __SwitchUiConstructor1<RefType: super::RefType> {
type Ctx<'a>: ConstMutSwitchUi<RefType = RefType>;
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a>;
@ -58,53 +59,47 @@ impl __SwitchUiConstructor1<ConstRef> for ConstUI<'_> {
type Ctx<'a> = ConstUI<'a>;
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a> {
todo!()
return ConstUI::wrap(ui);
}
}
impl __SwitchUiConstructor1<MutRef> for MutUI<'_> {
type Ctx<'a> = MutUI<'a>;
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a> {
todo!()
return MutUI::wrap(ui);
}
}
pub(super) fn render_table<
'a,
Ctx: ConstMutSwitchUi,
F: _ConstMutSwitchUiTableBaseCallback<Ctx::RefType>,
Constructor: __SwitchUiConstructor1<Ctx::RefType>,
RefType: super::RefType,
Constructor: __SwitchUiConstructor1<RefType>,
>(
ui: &mut Ui,
vscroll: bool,
columnsCount: usize,
headerClojure: &mut <F::Header as _ConstMutSwitchUiTableRowCallback<Ctx::RefType>>::Clojure<'_>,
header_render: impl _ConstMutSwitchUiTableHeaderCallback<RefType>,
) {
let mut rows_it = unsafe { uninitialized() };
TableBuilder::new(ui)
.striped(true) // Alternating row colors
.resizable(true)
.vscroll(vscroll)
.columns(Column::remainder(), columnsCount)
.header(20.0, |row| {
F::Header::render_row(
&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> {
rows_it =
header_render.render_header(&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> {
row,
__phantom: PhantomData::default(),
},
headerClojure,
)
});
})
.body(|mut body| {
for mut rowData in F::header_to_body(headerClojure) {
rows_it.consume(|row_render| {
body.row(20.0, |row| {
F::Body::render_row(
&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> {
row,
__phantom: PhantomData::default(),
},
&mut rowData,
)
});
}
row_render.render_row(&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> {
row,
__phantom: PhantomData::default(),
})
})
});
});
}

View File

@ -1,6 +1,4 @@
use super::{
_ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback, ConstRef, RefType,
};
use super::{_ConstMutSwitchUiTableHeaderCallback, RefType};
use eframe::emath::Numeric;
use std::ops::RangeInclusive;
@ -26,10 +24,7 @@ pub trait ConstMutSwitchUi {
fn space(&mut self);
fn horizontal<F: _ConstMutSwitchUiCallback<Self::RefType>>(
&mut self,
clojure: &mut F::Clojure<'_>,
);
fn horizontal(&mut self, clojure: impl _ConstMutSwitchUiCallback<Self::RefType>);
fn button<T>(
&mut self,
@ -40,29 +35,16 @@ pub trait ConstMutSwitchUi {
fn separator(&mut self);
fn scroll_area_2<F: _ConstMutSwitchUiCallback<Self::RefType>>(
&mut self,
clojure: &mut F::Clojure<'_>,
);
fn scroll_area_2(&mut self, clojure: impl _ConstMutSwitchUiCallback<Self::RefType>);
fn table<
'a,
F: _ConstMutSwitchUiTableBaseCallback<Self::RefType>
>(
fn table(
&mut self,
vscroll: bool,
columns: usize,
header_clojure: <F::Header as _ConstMutSwitchUiTableRowCallback<Self::RefType>>::Clojure<
'_,
>,
header: impl _ConstMutSwitchUiTableHeaderCallback<Self::RefType>,
);
}
pub trait _ConstMutSwitchUiCallback<RefType: super::RefType> {
type Clojure<'a>;
fn render<SubCtx: ConstMutSwitchUi<RefType = RefType>>(
ctx: &mut SubCtx,
clojure: &mut Self::Clojure<'_>,
);
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType = RefType>);
}

View File

@ -10,6 +10,9 @@
Альтернативный вариант - параметризовать тип ссылок...
В данном файле приведены только краткие выдержки из рассуждений.
![](https://www.meme-arsenal.com/memes/639e92214ce1ea090b2134dbd89c3cf1.jpg)
## 1
Начальный интерфейс содержал ассоциированный тип с самой ссылкой и методом
@ -684,3 +687,151 @@ trait _IteratorGetter {
```
Но способ работает только для очень простых случаев, так что малопригоден.
## 5
Что бы упростить функции, отвечающие за интерфейс и абстрагироваться от формата хранения данных,
работа с графом была переделана на контексты (почему-то всё всегда приходит к контекстам):
```rust
trait Ctx_Graph<G> {
type RefType: RefType;
type Vertex;
fn iter_vertices(&mut self, graph: &mut G) -> impl Iterator<Item = Self::Vertex>;
type Edge;
fn iter_edges_or_nothing(
&mut self,
vertex: &mut Self::Vertex,
) -> impl Iterator<Item = Option<Self::Edge>>;
fn get_edge_len_ptr(
&mut self,
edge: &mut Self::Edge,
) -> <Self::RefType as RefType>::Ref<'_, f64>;
fn set_vertex_to_update_state(&self, state: &mut UpdatePending);
}
fn draw_lengths_table<
G,
CtxUi: ConstMutSwitchUi,
CtxGraph: Ctx_Graph<G, RefType = CtxUi::RefType>,
>(
ctx_ui: &mut CtxUi,
ctx_graph: &mut CtxGraph,
graph: G,
update: <CtxUi::RefType as RefType>::Ref<'_, UpdatePending>,
) {
// ...
}
```
Тип графа передаётся параметром, а не ассоциированным типом, потому что в общем случае
контекст может работать с разными представлениями одновременно.
## 6
### 6.1
В результате очередной внезапной гениальной мысли, все трейты которые должны были быть
лямбдами, вместо ассоциированного типа для параметра теперь используют `self`. Источник
озарения - необходимость сделать эту функцию деструктором для передаваемых данных. Не
то что бы это нельзя было сделать с параметром, но новое способ оказался удобнее для
имплементации:
```rust
trait _ConstMutSwitchUiCallback<RefType: super::RefType> {
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType = RefType>);
}
trait ConstMutSwitchUi {
// ...
fn horizontal<F>(&mut self, clojure: impl _ConstMutSwitchUiCallback<Self::RefType>);
// ...
}
```
Такой способ более гибок корректен с точки зрения владения переданными для области данными.
### 6.2
Необходимость сделать этот метод деструктором возникла во время оптимизации трейтов для таблиц:
метод для отрисовки заголовка становится конвертером в итератор.
```rust
trait _ConstMutSwitchUiTableHeaderCallback<RefType: super::RefType> {
type RowRender: _ConstMutSwitchUiTableRowCallback<RefType=RefType>;
fn render_header<'a>(
self,
ctx: &mut impl ConstMutSwitchUiTableRow<RefType = RefType>,
) -> impl Iterator<Item = Self::RowRender>;
}
```
### 6.3
Итератор, который возвращает значения не может контролировать, что возвращённое значение не будет
использоваться после выдачи следующего. Чтобы это ограничить, вместо возврата итератор будет
передавать значение в полученную лямбду.
```rust
trait LambdaIterator {
type Item;
fn next<R>(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option<R>;
fn consume(mut self, receiver: impl FnMut(Self::Item)) where Self: Sized {
while self.next(|e| receiver(e)).is_some() {}
}
}
```
Метод `consume` добавлен просто для удобства. На новый трейт были заменены все итераторы в этом
модуле.
### 6.4
Но из-за того, что значение в параметр `self` после `_ConstMutSwitchUiTableHeaderCallback::render_header`
не существует, нельзя просто взять и вернуть что-то с ним связанное (вообще должно быть можно, но
было много красного и лень разбираться). Решение такое же как и с итератором: "возвращать" в лямбду:
```rust
trait _ConstMutSwitchUiTableHeaderCallback<RefType: super::RefType> {
type RowRender: _ConstMutSwitchUiTableRowCallback<RefType>;
type RowsIterator: LambdaIterator<Item = Self::RowRender>;
fn render_header(
self,
ctx: &mut impl ConstMutSwitchUiTableRow<RefType = RefType>,
render_body: impl FnOnce(Self::RowsIterator),
);
}
```
### 6.5
Решение передавать в метод отрисовки заголовка лямбду выглядело прекрасно, пока не выяснилось, что
для получения дескриптора отрисовки тела таблицы нужно выйти из этого метода, что случается после
того как передаётся лямбда которая его использует. Пришлось всё-таки сразиться с borrow checker и найти
ошибку, которая мешала возвращать итератор из этого метода и вернуться на этап `6.2`.
### 6.6
Пользоваться контекстом графа внутри лямбды, переданной в итератор от этого контекста по очевидным
причинам нельзя, поэтому возвращаемые итераторы были расширены:
```rust
trait Ctx_Graph<G> {
// ...
type Vertex;
type VerticesIterator<'a>: LambdaIterator<Item = (&'a mut Self, Self::Vertex)>
where
Self: 'a;
fn iter_vertices(&mut self, graph: &mut G) -> Self::VerticesIterator<'_>;
type Edge;
fn iter_edges_or_nothing(
&mut self,
vertex: &mut Self::Vertex,
) -> impl LambdaIterator<Item = (&mut Self, Option<Self::Edge>)>;
// ...
}
```

View File

@ -1,173 +1,201 @@
use crate::UpdatePending;
use crate::graph::{CompleteGraph, Edge, EdgesVec, VerticesVec};
use crate::gui::const_mut_switch::{_ConstMutSwitchUiCallback, _ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUi, ConstMutSwitchUiTableRow, RefGetter, RefType, _IteratorGetter};
use eframe::egui::{ScrollArea, Ui};
use egui_extras::{Column, TableBuilder};
use std::collections::HashSet;
use std::ops::{Index, IndexMut};
use crate::gui::const_mut_switch::{
_ConstMutSwitchUiCallback, _ConstMutSwitchUiTableHeaderCallback,
_ConstMutSwitchUiTableRowCallback, ConstMutSwitchUi, ConstMutSwitchUiTableRow, RefType,
};
use crate::{LambdaIterator, UpdatePending};
use std::ptr::NonNull;
pub fn draw_lengths_table<'a, Ctx: ConstMutSwitchUi>(
ctx: &mut Ctx,
graph: <Ctx::RefType as RefType>::Ref<'a, CompleteGraph<()>>,
update: <Ctx::RefType as RefType>::Ref<'a, UpdatePending>,
pub trait Ctx_Graph<G> {
type RefType: RefType;
type Vertex;
type VerticesIterator<'a>: LambdaIterator<Item=(&'a mut Self, Self::Vertex)>
where
Self: 'a;
fn iter_vertices(&mut self, graph: &mut G) -> Self::VerticesIterator<'_>;
type Edge;
fn iter_edges_or_nothing(
&mut self,
vertex: &mut Self::Vertex,
) -> impl LambdaIterator<Item=(&mut Self, Option<Self::Edge>)>;
fn get_edge_len_ptr(
&mut self,
edge: &mut Self::Edge,
) -> <Self::RefType as RefType>::Ref<'_, f64>;
fn set_vertex_to_update_state(&self, vertex: &Self::Vertex, state: &mut UpdatePending);
fn vertices_count(&self) -> usize;
}
pub fn draw_lengths_table<
G,
CtxUi: ConstMutSwitchUi,
CtxGraph: Ctx_Graph<G, RefType=CtxUi::RefType>,
>(
ctx_ui: &mut CtxUi,
ctx_graph: &mut CtxGraph,
graph: G,
update: <CtxUi::RefType as RefType>::Ref<'_, UpdatePending>,
) {
ctx.scroll_area_2::<ScrollArea>(&mut (graph, update));
ctx_ui.scroll_area_2(ScrollArea {
ctx_graph,
graph,
update,
});
}
struct ScrollArea<'g, 'u, G, CtxGraph: Ctx_Graph<G>> {
ctx_graph: &'g mut CtxGraph,
graph: G,
update: <CtxGraph::RefType as RefType>::Ref<'u, UpdatePending>,
}
struct ScrollArea {}
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for ScrollArea {
type Clojure<'a> = (RT::Ref<'a, CompleteGraph<()>>, RT::Ref<'a, UpdatePending>);
fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>(
ctx: &mut SubCtx,
mut clojure: &mut (RT::Ref<'_, CompleteGraph<()>>, RT::Ref<'_, UpdatePending>),
) {
ctx.table::<Table>(false, clojure.0.vertex_count(), HeaderData {
update_state: clojure.1,
edges: CompleteGraph::<()>,
vertices: (),
})
}
impl<G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiCallback<CtxGraph::RefType>
for ScrollArea<'_, '_, G, CtxGraph>
{
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType=CtxGraph::RefType>) {
ctx.table(
false,
self.ctx_graph.vertices_count(),
Header {
ctx_graph: self.ctx_graph,
graph: self.graph,
update: self.update,
},
)
}
}
struct Table{}
impl <RT: RefType>_ConstMutSwitchUiTableBaseCallback<RT> for Table {
type Header = Header;
type Body = Body;
struct Header<'g, 'u, G, CtxGraph: Ctx_Graph<G>> {
ctx_graph: &'g mut CtxGraph,
graph: G,
update: <CtxGraph::RefType as RefType>::Ref<'u, UpdatePending>,
}
fn header_to_body<'a>(header: &mut <Self::Header as _ConstMutSwitchUiTableRowCallback<RT>>::Clojure<'a>) -> impl Iterator<Item=<Self::Body as _ConstMutSwitchUiTableRowCallback<RT>>::Clojure<'a>> {
struct ItGet{};
impl _IteratorGetter for ItGet {
type C = VerticesVec;
type T<'a> = HashSet<usize>;
impl<'g, 'u, G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiTableHeaderCallback<CtxGraph::RefType>
for Header<'g, 'u, G, CtxGraph>
{
type RowRender = Row<'g, 'u, G, CtxGraph>;
fn get_const_iterator<'a>(cont: &'a Self::C) -> impl Iterator<Item=&Self::T<'a>> {
return cont.iter_indexed()
}
type RowsIterator = RowsIterators<'g, 'u, G, CtxGraph>;
fn get_mut_iterator<'a>(cont: &'a mut Self::C) -> impl Iterator<Item=&mut Self::T<'a>> {
return cont.iter_indexed_mut()
}
}
fn render_header(
mut self,
row: &mut impl ConstMutSwitchUiTableRow<RefType=CtxGraph::RefType>,
) -> Self::RowsIterator {
row.cell(CornerCell {});
return header.vertices.iter_indexed().enumerate().map(|(ri, (vi, eis))| {
return RowData{
update_state: header.update_state,
edges: header.edges,
row_id: ri,
vertex_id: vi,
vertex_edges_ids: eis,
}
})
let mut column_id = 0;
self.ctx_graph
.iter_vertices(&mut self.graph)
.consume(|(ctx_graph, v)| {
column_id += 1;
// let update = &mut self.update;
let data = TitleCell {
ctx_graph: ctx_graph,
row_id: column_id,
vertex: &v,
update: &mut self.update,
};
row.cell(data)
});
return RowsIterators {
vertices: self.ctx_graph.iter_vertices(&mut self.graph),
update: self.update,
row_id: 0,
};
}
}
struct HeaderData<'us, 'es, 'eis, RT: RefType> {
update_state: RT::Ref<'us, UpdatePending>,
edges: RT::Ref<'es, EdgesVec<()>>,
vertices: RT::Ref<'eis, VerticesVec>,
}
struct Header {}
impl<RT: RefType> _ConstMutSwitchUiTableRowCallback<RT> for Header {
type Clojure<'a> = HeaderData<'a, 'a, 'a, RT>;
fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RT>>(
row: &mut Row,
clojure: &mut Self::Clojure<'_>,
) {
row.cell::<CornerCell>(&mut ());
for i in 0..clojure.vertices.len() {
row.cell::<Title>(&mut (i, NonNull::from_ref(&clojure.update_state)));
}
struct CornerCell {}
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for CornerCell {
type Clojure<'a> = ();
fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>(ctx: &mut SubCtx, _: &mut ()) {
ctx.label("#");
}
}
}
}
struct RowData<'us, 'es, 'eis, RT: RefType> {
update_state: RT::Ref<'us, UpdatePending>,
edges: RT::Ref<'es, EdgesVec<()>>,
struct RowsIterators<'g, 'u, G, CtxGraph: Ctx_Graph<G> + 'g> {
vertices: CtxGraph::VerticesIterator<'g>,
update: <CtxGraph::RefType as RefType>::Ref<'u, UpdatePending>,
row_id: usize,
vertex_id: usize,
vertex_edges_ids: RT::Ref<'eis, HashSet<usize>>,
}
struct Body {}
impl<RT: RefType> _ConstMutSwitchUiTableRowCallback<RT> for Body {
type Clojure<'a> = RowData<'a, 'a, 'a, RT>;
impl<'g, 'u, G, CtxGraph: Ctx_Graph<G> + 'g> LambdaIterator for RowsIterators<'g, 'u, G, CtxGraph> {
type Item = Row<'g, 'u, G, CtxGraph>;
fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RT>>(
row: &mut Row,
clojure: &mut Self::Clojure<'_>,
) {
row.cell::<Title>(&mut (clojure.row_id, NonNull::from_ref(&clojure.update_state)));
// not map because a borrow checker doesn't allow returning from lambda.
// raw pointer because a borrow checker.
let mut local_edges =
Vec::<(usize, NonNull<f64>)>::with_capacity(clojure.vertex_edges_ids.len());
for ei in clojure.vertex_edges_ids.iter() {
let mut e = RT::_get(
&mut clojure.edges,
|ee| ee.index(*ei),
|ee| ee.index_mut(*ei),
);
let another_vertex = e.another(clojure.vertex_id);
let length_storage = RT::_get(&mut e, |e| &e.length, |e| &mut e.length);
let length_storage = unsafe { RT::_to_ptr(length_storage) };
local_edges.push((another_vertex, length_storage));
}
local_edges.sort_by_key(|(k, _)| *k);
for (column_index, cell_data) in local_edges.iter_mut().enumerate() {
if (column_index == clojure.row_id) {
row.cell::<EmptyCell>(&mut ());
}
row.cell::<Cell>(&mut unsafe { RT::_from_ptr(cell_data.1) });
}
struct Cell {}
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for Cell {
type Clojure<'a> = RT::Ref<'a, f64>;
fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>(
ctx: &mut SubCtx,
clojure: &mut Self::Clojure<'_>,
) {
ctx.slider(0.0..=10.0, 0.1, clojure);
}
}
struct EmptyCell {}
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for EmptyCell {
type Clojure<'a> = ();
fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>(_: &mut SubCtx, _: &mut ()) {}
}
fn next<R>(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option<R> {
return self.vertices.next(|(ctx, v)| {
self.row_id += 1;
return receiver(Row {
ctx_graph: ctx,
vertex: v,
row_id: self.row_id,
update: NonNull::from_mut(&mut self.update),
});
});
}
}
struct Title {}
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for Title {
type Clojure<'a> = (usize, NonNull<RT::Ref<'a, UpdatePending>>);
struct Row<'g, 'u, G, CtxGraph: Ctx_Graph<G>> {
ctx_graph: &'g mut CtxGraph,
vertex: CtxGraph::Vertex,
row_id: usize,
update: NonNull<<CtxGraph::RefType as RefType>::Ref<'u, UpdatePending>>,
}
fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>(
ctx: &mut SubCtx,
clojure: &mut Self::Clojure<'_>,
) {
ctx.label(clojure.0.to_string().as_str());
ctx.button("-", unsafe { clojure.1.as_mut() }, |s| {
*s = UpdatePending::Remove(clojure.0)
})
impl<G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiTableRowCallback<CtxGraph::RefType>
for Row<'_, '_, G, CtxGraph>
{
fn render_row(mut self, row: &mut impl ConstMutSwitchUiTableRow<RefType=CtxGraph::RefType>) {
row.cell(TitleCell {
ctx_graph: self.ctx_graph,
row_id: self.row_id,
vertex: &self.vertex,
update: unsafe { self.update.as_mut() },
});
self.ctx_graph
.iter_edges_or_nothing(&mut self.vertex)
.consume(|(ctx_graph, e)| {
match e {
None => row.cell(EmptyCell {}),
Some(mut e) => row.cell(LengthCell {
length: &mut ctx_graph.get_edge_len_ptr(&mut e),
})
}
})
}
}
struct CornerCell {}
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for CornerCell {
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType=RT>) {
ctx.label("#");
}
}
struct TitleCell<'g, 'v, 'rr, 'r: 'rr, G, CtxGraph: Ctx_Graph<G>> {
ctx_graph: &'g mut CtxGraph,
row_id: usize,
vertex: &'v CtxGraph::Vertex,
update: &'rr mut <CtxGraph::RefType as RefType>::Ref<'r, UpdatePending>,
}
impl<G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiCallback<CtxGraph::RefType>
for TitleCell<'_, '_, '_, '_, G, CtxGraph>
{
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType=CtxGraph::RefType>) {
ctx.label(self.row_id.to_string().as_str());
ctx.button("-", self.update, |s| {
self.ctx_graph.set_vertex_to_update_state(self.vertex, s)
});
}
}
struct LengthCell<'rr, 'r: 'rr, RT: RefType> {
length: &'rr mut RT::Ref<'r, f64>,
}
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for LengthCell<'_, '_, RT> {
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType=RT>) {
ctx.slider(0.0..=10.0, 0.1, self.length);
}
}
struct EmptyCell {}
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for EmptyCell {
fn render(self, _: &mut impl ConstMutSwitchUi<RefType=RT>) {}
}

View File

@ -1,6 +1,6 @@
use crate::graph::{Edge, EdgesVec, VerticesVec};
use eframe::egui;
use eframe::egui::{Align2, Color32, FontDefinitions, Pos2, Rect, StrokeKind, Ui, Vec2};
use eframe::egui::{Align2, Color32, Pos2, Rect, StrokeKind, Ui, Vec2};
use eframe::epaint::{CornerRadius, Stroke};
pub fn render_graph<D>(

View File

@ -0,0 +1,28 @@
pub trait LambdaIterator {
type Item;
fn next<R>(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option<R>;
fn consume(mut self, mut receiver: impl FnMut(Self::Item))
where
Self: Sized,
{
while self.next(|e| receiver(e)).is_some() {}
}
}
pub fn make_lambda<I: Iterator>(it: I) -> impl LambdaIterator<Item = I::Item> {
return LambdaIteratorConverter { it };
}
struct LambdaIteratorConverter<I: Iterator> {
it: I,
}
impl<I: Iterator> LambdaIterator for LambdaIteratorConverter<I> {
type Item = I::Item;
fn next<R>(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option<R> {
return self.it.next().map(receiver);
}
}

View File

@ -1,7 +1,9 @@
pub mod graph;
pub mod gui;
mod lambda_iterator;
mod update_pending;
mod weighted_random;
pub use lambda_iterator::{LambdaIterator, make_lambda};
pub use update_pending::{UpdatePending, UpdateTarget};
pub use weighted_random::{weighted_random, weighted_random_index};