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

View File

@ -6,9 +6,9 @@ mod r#trait;
pub use r#const::ConstUI; pub use r#const::ConstUI;
pub use r#mut::MutUI; 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; use table::render_table;
pub use table::{ pub use table::{
_ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUiTableRow, _ConstMutSwitchUiTableHeaderCallback, _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUiTableRow,
}; };
pub use r#trait::{_ConstMutSwitchUiCallback, ConstMutSwitchUi}; 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 crate::gui::const_mut_switch::r#trait::_ConstMutSwitchUiCallback;
use eframe::egui::{ScrollArea, Ui}; use eframe::egui::{ScrollArea, Ui};
use eframe::emath::Numeric; use eframe::emath::Numeric;
@ -8,6 +8,12 @@ pub struct MutUI<'ui> {
ui: &'ui mut Ui, ui: &'ui mut Ui,
} }
impl<'ui> MutUI<'ui> {
pub fn wrap(ui: &'ui mut Ui) -> Self {
return Self { ui };
}
}
impl ConstMutSwitchUi for MutUI<'_> { impl ConstMutSwitchUi for MutUI<'_> {
type RefType = MutRef; type RefType = MutRef;
@ -15,7 +21,12 @@ impl ConstMutSwitchUi for MutUI<'_> {
self.ui.label(s); 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); crate::gui::slider(self.ui, *storage, range, step);
} }
@ -33,9 +44,8 @@ impl ConstMutSwitchUi for MutUI<'_> {
self.ui.label(""); self.ui.label("");
} }
fn horizontal<I: _ConstMutSwitchUiCallback<MutRef>>(&mut self, clojure: &mut I::Clojure<'_>) { fn horizontal(&mut self, scope: impl _ConstMutSwitchUiCallback<MutRef>) {
self.ui self.ui.horizontal(|ui| scope.render(&mut MutUI { ui }));
.horizontal(|ui| I::render(&mut MutUI { ui }, clojure));
} }
fn button<T>(&mut self, label: &str, storage: &mut &mut T, on_click: impl FnOnce(&mut T)) { 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(); self.ui.separator();
} }
fn scroll_area_2<F: _ConstMutSwitchUiCallback<Self::RefType>>( fn scroll_area_2(&mut self, scope: impl _ConstMutSwitchUiCallback<MutRef>) {
&mut self,
clojure: &mut F::Clojure<'_>,
) {
ScrollArea::both() ScrollArea::both()
.auto_shrink([false, false]) .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< fn table(
'a,
F: _ConstMutSwitchUiTableBaseCallback<MutRef, Body = _B>,
_B: _ConstMutSwitchUiTableRowCallback<MutRef, Clojure<'a> = _BC>,
_BC,
>(
&mut self, &mut self,
vscroll: bool, vscroll: bool,
columns: usize, columns: usize,
header_clojure: <F::Header as _ConstMutSwitchUiTableRowCallback<MutRef>>::Clojure<'_>, header: impl _ConstMutSwitchUiTableHeaderCallback<Self::RefType>,
body_data: impl Iterator<Item = _BC>,
) { ) {
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::ops::Deref;
use std::ptr::NonNull; use std::ptr::NonNull;
pub trait RefType { 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>( fn _get<'r, 'f: 'r, 's: 'f, S, M>(
storge: &'f mut Self::Ref<'s, S>, 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 _to_ptr<T>(r: Self::Ref<'_, T>) -> NonNull<T>;
unsafe fn _from_ptr<'a, T>(r: NonNull<T>) -> Self::Ref<'a, 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 {} pub struct ConstRef {}
@ -61,12 +34,6 @@ impl RefType for ConstRef {
unsafe fn _from_ptr<'a, T>(r: NonNull<T>) -> Self::Ref<'a, T> { unsafe fn _from_ptr<'a, T>(r: NonNull<T>) -> Self::Ref<'a, T> {
return r.as_ref(); 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 {} 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> { unsafe fn _from_ptr<'a, T>(mut r: NonNull<T>) -> Self::Ref<'a, T> {
return r.as_mut(); 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 eframe::egui::Ui;
use egui_extras::{Column, TableBuilder, TableRow}; use egui_extras::{Column, TableBuilder, TableRow};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::uninitialized;
pub trait _ConstMutSwitchUiTableBaseCallback<RefType: super::RefType> { pub trait _ConstMutSwitchUiTableHeaderCallback<RefType: super::RefType> {
type Header: _ConstMutSwitchUiTableRowCallback<RefType>; type RowRender: _ConstMutSwitchUiTableRowCallback<RefType>;
type Body: _ConstMutSwitchUiTableRowCallback<RefType>; type RowsIterator: LambdaIterator<Item = Self::RowRender>;
fn header_to_body<'a>( fn render_header(
header: &mut <Self::Header as _ConstMutSwitchUiTableRowCallback<RefType>>::Clojure<'a>, self,
) -> impl Iterator<Item = <Self::Body as _ConstMutSwitchUiTableRowCallback<RefType>>::Clojure<'a>>; ctx: &mut impl ConstMutSwitchUiTableRow<RefType = RefType>,
) -> Self::RowsIterator;
} }
pub trait _ConstMutSwitchUiTableRowCallback<RefType: super::RefType> { pub trait _ConstMutSwitchUiTableRowCallback<RefType: super::RefType> {
type Clojure<'a>; fn render_row(self, row: &mut impl ConstMutSwitchUiTableRow<RefType = RefType>);
fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RefType>>(
row: &mut Row,
clojure: &mut Self::Clojure<'_>,
);
} }
pub trait ConstMutSwitchUiTableRow { pub trait ConstMutSwitchUiTableRow {
type RefType: RefType; 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< pub(super) struct ConstMutSwitchUiTableRowImpl<
@ -42,13 +44,12 @@ impl<RefType: super::RefType, Constructor: __SwitchUiConstructor1<RefType>> Cons
{ {
type RefType = RefType; type RefType = RefType;
fn cell<F: _ConstMutSwitchUiCallback<Self::RefType>>(&mut self, clojure: &mut F::Clojure<'_>) { fn cell(&mut self, cell: impl _ConstMutSwitchUiCallback<Self::RefType>) {
self.row self.row.col(|ui| cell.render(&mut Constructor::__wrap(ui)));
.col(|ui| F::render(&mut Constructor::__wrap(ui), clojure));
} }
} }
trait __SwitchUiConstructor1<RefType: super::RefType> { pub(super) trait __SwitchUiConstructor1<RefType: super::RefType> {
type Ctx<'a>: ConstMutSwitchUi<RefType = RefType>; type Ctx<'a>: ConstMutSwitchUi<RefType = RefType>;
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a>; fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a>;
@ -58,53 +59,47 @@ impl __SwitchUiConstructor1<ConstRef> for ConstUI<'_> {
type Ctx<'a> = ConstUI<'a>; type Ctx<'a> = ConstUI<'a>;
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a> { fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a> {
todo!() return ConstUI::wrap(ui);
} }
} }
impl __SwitchUiConstructor1<MutRef> for MutUI<'_> { impl __SwitchUiConstructor1<MutRef> for MutUI<'_> {
type Ctx<'a> = MutUI<'a>; type Ctx<'a> = MutUI<'a>;
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a> { fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a> {
todo!() return MutUI::wrap(ui);
} }
} }
pub(super) fn render_table< pub(super) fn render_table<
'a, RefType: super::RefType,
Ctx: ConstMutSwitchUi, Constructor: __SwitchUiConstructor1<RefType>,
F: _ConstMutSwitchUiTableBaseCallback<Ctx::RefType>,
Constructor: __SwitchUiConstructor1<Ctx::RefType>,
>( >(
ui: &mut Ui, ui: &mut Ui,
vscroll: bool, vscroll: bool,
columnsCount: usize, 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) TableBuilder::new(ui)
.striped(true) // Alternating row colors .striped(true) // Alternating row colors
.resizable(true) .resizable(true)
.vscroll(vscroll) .vscroll(vscroll)
.columns(Column::remainder(), columnsCount) .columns(Column::remainder(), columnsCount)
.header(20.0, |row| { .header(20.0, |row| {
F::Header::render_row( rows_it =
&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> { header_render.render_header(&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> {
row, row,
__phantom: PhantomData::default(), __phantom: PhantomData::default(),
}, });
headerClojure,
)
}) })
.body(|mut body| { .body(|mut body| {
for mut rowData in F::header_to_body(headerClojure) { rows_it.consume(|row_render| {
body.row(20.0, |row| { body.row(20.0, |row| {
F::Body::render_row( row_render.render_row(&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> {
&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> { row,
row, __phantom: PhantomData::default(),
__phantom: PhantomData::default(), })
}, })
&mut rowData, });
)
});
}
}); });
} }

View File

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

View File

@ -10,6 +10,9 @@
Альтернативный вариант - параметризовать тип ссылок... Альтернативный вариант - параметризовать тип ссылок...
В данном файле приведены только краткие выдержки из рассуждений.
![](https://www.meme-arsenal.com/memes/639e92214ce1ea090b2134dbd89c3cf1.jpg)
## 1 ## 1
Начальный интерфейс содержал ассоциированный тип с самой ссылкой и методом Начальный интерфейс содержал ассоциированный тип с самой ссылкой и методом
@ -683,4 +686,152 @@ 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::gui::const_mut_switch::{
use crate::graph::{CompleteGraph, Edge, EdgesVec, VerticesVec}; _ConstMutSwitchUiCallback, _ConstMutSwitchUiTableHeaderCallback,
use crate::gui::const_mut_switch::{_ConstMutSwitchUiCallback, _ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUi, ConstMutSwitchUiTableRow, RefGetter, RefType, _IteratorGetter}; _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUi, ConstMutSwitchUiTableRow, RefType,
use eframe::egui::{ScrollArea, Ui}; };
use egui_extras::{Column, TableBuilder}; use crate::{LambdaIterator, UpdatePending};
use std::collections::HashSet;
use std::ops::{Index, IndexMut};
use std::ptr::NonNull; use std::ptr::NonNull;
pub fn draw_lengths_table<'a, Ctx: ConstMutSwitchUi>( pub trait Ctx_Graph<G> {
ctx: &mut Ctx, type RefType: RefType;
graph: <Ctx::RefType as RefType>::Ref<'a, CompleteGraph<()>>,
update: <Ctx::RefType as RefType>::Ref<'a, UpdatePending>, 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<G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiCallback<CtxGraph::RefType>
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for ScrollArea { for ScrollArea<'_, '_, G, CtxGraph>
type Clojure<'a> = (RT::Ref<'a, CompleteGraph<()>>, RT::Ref<'a, UpdatePending>); {
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType=CtxGraph::RefType>) {
fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>( ctx.table(
ctx: &mut SubCtx, false,
mut clojure: &mut (RT::Ref<'_, CompleteGraph<()>>, RT::Ref<'_, UpdatePending>), self.ctx_graph.vertices_count(),
) { Header {
ctx.table::<Table>(false, clojure.0.vertex_count(), HeaderData { ctx_graph: self.ctx_graph,
update_state: clojure.1, graph: self.graph,
edges: CompleteGraph::<()>, update: self.update,
vertices: (), },
}) )
}
} }
} }
struct Table{} struct Header<'g, 'u, G, CtxGraph: Ctx_Graph<G>> {
impl <RT: RefType>_ConstMutSwitchUiTableBaseCallback<RT> for Table { ctx_graph: &'g mut CtxGraph,
type Header = Header; graph: G,
type Body = Body; 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>> { impl<'g, 'u, G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiTableHeaderCallback<CtxGraph::RefType>
struct ItGet{}; for Header<'g, 'u, G, CtxGraph>
impl _IteratorGetter for ItGet { {
type C = VerticesVec; type RowRender = Row<'g, 'u, G, CtxGraph>;
type T<'a> = HashSet<usize>;
fn get_const_iterator<'a>(cont: &'a Self::C) -> impl Iterator<Item=&Self::T<'a>> { type RowsIterator = RowsIterators<'g, 'u, G, CtxGraph>;
return cont.iter_indexed()
}
fn get_mut_iterator<'a>(cont: &'a mut Self::C) -> impl Iterator<Item=&mut Self::T<'a>> { fn render_header(
return cont.iter_indexed_mut() 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{ let mut column_id = 0;
update_state: header.update_state, self.ctx_graph
edges: header.edges, .iter_vertices(&mut self.graph)
row_id: ri, .consume(|(ctx_graph, v)| {
vertex_id: vi, column_id += 1;
vertex_edges_ids: eis, // 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 RowsIterators<'g, 'u, G, CtxGraph: Ctx_Graph<G> + 'g> {
vertices: CtxGraph::VerticesIterator<'g>,
struct HeaderData<'us, 'es, 'eis, RT: RefType> { update: <CtxGraph::RefType as RefType>::Ref<'u, UpdatePending>,
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<()>>,
row_id: usize, row_id: usize,
vertex_id: usize,
vertex_edges_ids: RT::Ref<'eis, HashSet<usize>>,
} }
struct Body {} impl<'g, 'u, G, CtxGraph: Ctx_Graph<G> + 'g> LambdaIterator for RowsIterators<'g, 'u, G, CtxGraph> {
impl<RT: RefType> _ConstMutSwitchUiTableRowCallback<RT> for Body { type Item = Row<'g, 'u, G, CtxGraph>;
type Clojure<'a> = RowData<'a, 'a, 'a, RT>;
fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RT>>( fn next<R>(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option<R> {
row: &mut Row, return self.vertices.next(|(ctx, v)| {
clojure: &mut Self::Clojure<'_>, self.row_id += 1;
) { return receiver(Row {
row.cell::<Title>(&mut (clojure.row_id, NonNull::from_ref(&clojure.update_state))); ctx_graph: ctx,
vertex: v,
// not map because a borrow checker doesn't allow returning from lambda. row_id: self.row_id,
// raw pointer because a borrow checker. update: NonNull::from_mut(&mut self.update),
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 ()) {}
}
} }
} }
struct Title {} struct Row<'g, 'u, G, CtxGraph: Ctx_Graph<G>> {
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for Title { ctx_graph: &'g mut CtxGraph,
type Clojure<'a> = (usize, NonNull<RT::Ref<'a, UpdatePending>>); vertex: CtxGraph::Vertex,
row_id: usize,
update: NonNull<<CtxGraph::RefType as RefType>::Ref<'u, UpdatePending>>,
}
fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>( impl<G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiTableRowCallback<CtxGraph::RefType>
ctx: &mut SubCtx, for Row<'_, '_, G, CtxGraph>
clojure: &mut Self::Clojure<'_>, {
) { fn render_row(mut self, row: &mut impl ConstMutSwitchUiTableRow<RefType=CtxGraph::RefType>) {
ctx.label(clojure.0.to_string().as_str()); row.cell(TitleCell {
ctx.button("-", unsafe { clojure.1.as_mut() }, |s| { ctx_graph: self.ctx_graph,
*s = UpdatePending::Remove(clojure.0) 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 crate::graph::{Edge, EdgesVec, VerticesVec};
use eframe::egui; 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}; use eframe::epaint::{CornerRadius, Stroke};
pub fn render_graph<D>( 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 graph;
pub mod gui; pub mod gui;
mod lambda_iterator;
mod update_pending; mod update_pending;
mod weighted_random; mod weighted_random;
pub use lambda_iterator::{LambdaIterator, make_lambda};
pub use update_pending::{UpdatePending, UpdateTarget}; pub use update_pending::{UpdatePending, UpdateTarget};
pub use weighted_random::{weighted_random, weighted_random_index}; pub use weighted_random::{weighted_random, weighted_random_index};