Attempt 1 to make mutability-independent UI

This commit is contained in:
Andrew Golovashevich 2026-02-20 16:42:17 +03:00
parent 088cf8d194
commit f295dc7a12
16 changed files with 1423 additions and 129 deletions

View File

@ -3,8 +3,6 @@ mod ant;
mod config;
mod update_state;
pub use bgtu_ai_utility::graph::{VerticesVec};
use bgtu_ai_utility::graph::{Edge as _BaseEdge, EdgesVec as _BaseEdgesVec};
pub(crate) use ant::Ant;
pub(crate) use config::AntsSimulationConfig;
pub(crate) use state::AntsSimulationState;

View File

@ -2,17 +2,21 @@ use bgtu_ai_utility::graph::CompleteGraph;
use bgtu_ai_utility::UpdatePending;
use crate::algo::{AntsSimulationConfig, AntsSimulationState};
pub(crate)enum GlobalState {
Edit,
Running,
pub(crate) enum AntsVisualisationData<'cfg> {
Edit {
config: &'cfg mut AntsSimulationConfig,
graph: &'cfg mut CompleteGraph<()>,
ants_per_vertex: usize,
vertex_update: UpdatePending,
},
Running {
simulation: AntsSimulationState<'cfg>,
ants_per_vertex: usize,
vertex_locations: Vec<(f32, f32)>,
},
}
pub(crate) struct MyApp {
pub graph: CompleteGraph<()>,
pub simulation: AntsSimulationState,
pub config: AntsSimulationConfig,
pub vertex_update: UpdatePending,
pub state: GlobalState,
pub vertex_locations: Vec<(f32, f32)>,
pub(crate) struct MyApp<'cfg> {
pub state: AntsVisualisationData<'cfg>,
pub ants_per_vertex: usize,
}
@ -30,7 +34,7 @@ impl MyApp {
q: 1.0,
r: 0.5,
},
state: GlobalState::Edit {},
state: AntsVisualisationData::Edit {},
vertex_locations: Vec::new(),
ants_per_vertex: 1,
};

View File

@ -1,4 +1,4 @@
use super::{GlobalState, MyApp};
use super::{AntsVisualisationData, MyApp};
use crate::algo::updateState;
use bgtu_ai_utility::gui::render_graph;
use eframe::egui::Ui;
@ -7,7 +7,7 @@ pub(crate) fn graph_with_controls(ui: &mut Ui, data: &mut MyApp) {
ui.vertical(|ui| {
ui.horizontal(|ui| {
if ui.button("Exit").clicked() {
data.state = GlobalState::Edit
data.state = AntsVisualisationData::Edit
}
if ui.button("Step").clicked() {
@ -17,7 +17,7 @@ pub(crate) fn graph_with_controls(ui: &mut Ui, data: &mut MyApp) {
});
draw_ants(ui, data);
if ui.input(|i| i.viewport().close_requested()) {
data.state = GlobalState::Edit
data.state = AntsVisualisationData::Edit
}
});
}

View File

@ -1,49 +1,88 @@
use std::collections::HashSet;
use eframe::egui::Ui;
use bgtu_ai_utility::gui::labeled_slider;
use bgtu_ai_utility::gui::lengths_table::draw_lengths_table;
use crate::algo::AntsSimulationConfig;
use bgtu_ai_utility::UpdatePending;
use crate::algo::Ant;
use super::{GlobalState, MyApp};
use bgtu_ai_utility::graph::CompleteGraph;
use bgtu_ai_utility::gui::const_mut_switch::{_ConstMutSwitchUiCallback, ConstMutSwitchUi, RefType, RefGetter};
pub(crate) fn input(ui: &mut Ui, data: &mut MyApp) {
let mut run: bool = false;
pub(crate) fn input<Ctx: ConstMutSwitchUi + RefGetter>(
ctx: &mut Ctx,
mut config: Ctx::Ref<'_, AntsSimulationConfig>,
mut graph: Ctx::RefType::Ref<'_, CompleteGraph<()>>,
mut ants_per_vertex: Ctx::RefType::Ref<'_, usize>,
mut vertex_update: Ctx::RefType::Ref<'_, UpdatePending>,
mut launch: Ctx::RefType::Ref<'_, bool>,
) {
type Ref<'a, T> = <<Ctx as ConstMutSwitchUi>::RefType as RefType>::Ref<'a, T>;
labeled_slider(
ui,
ctx.labeled_slider(
"Ferment weight",
&mut data.config.ferment_weight,
0.0..=1.0,
0.001,
&mut Ctx::_get(
&mut config,
|config| &config.ferment_weight,
|config| &mut config.ferment_weight,
),
);
ui.label("");
labeled_slider(
ui,
ctx.space();
ctx.labeled_slider(
"Heuristic coefficient",
&mut data.config.heuristic_coefficient,
0.0..=1.0,
0.001,
&mut Ctx::_get(
&mut config,
|config| &config.heuristic_coefficient,
|config| &mut config.heuristic_coefficient,
),
);
ui.label("");
labeled_slider(ui, "Q", &mut data.config.q, 0.0..=1.0, 0.001);
ui.label("");
labeled_slider(ui, "r", &mut data.config.r, 0.0..=1.0, 0.001);
ui.label("");
labeled_slider(
ui,
ctx.space();
ctx.labeled_slider(
"Q",
0.0..=1.0,
0.001,
&mut Ctx::_get(&mut config, |config| &config.q, |config| &mut config.q),
);
ctx.space();
ctx.labeled_slider(
"R",
0.0..=1.0,
0.001,
&mut Ctx::_get(&mut config, |config| &config.r, |config| &mut config.r),
);
ctx.space();
ctx.labeled_slider(
"Ants per vertex",
&mut data.ants_per_vertex,
1..=100,
1.0,
&mut Ctx::_get(&mut ants_per_vertex, |apv| apv, |apv| apv),
);
ui.label("");
ui.horizontal(|ui| {
if ui.button("Add vertex").clicked() {
data.vertex_update = UpdatePending::Add;
ctx.space();
struct ControlsRow {}
impl _ConstMutSwitchUiCallback<Ctx::RefType> for ControlsRow {
type Clojure = (Ref<'_, UpdatePending>, Ref<'_, bool>);
fn render<SubCtx: ConstMutSwitchUi<RefType = Ctx::RefType>>(
ctx: &mut SubCtx,
mut clojure: (Ref<'_, UpdatePending>, Ref<'_, bool>),
) {
ctx.button("Add vertex", &mut clojure.0, |d| *d = UpdatePending::Add);
ctx.separator();
ctx.button("Run", &mut clojure.1, |d| *d = true);
}
ui.separator();
run = ui.button("Run").clicked();
});
}
ctx.horizontal::<ControlsRow>(&mut (vertex_update, launch))
/*
draw_lengths_table(
ui,
@ -62,12 +101,13 @@ pub(crate) fn input(ui: &mut Ui, data: &mut MyApp) {
)
}
data.state = GlobalState::Running;
data.state = AntsVisualisationData::Running;
data.vertex_locations = coords;
let allowed_locations = data
.simulation
.graph.vertices
.graph
.vertices
.iter_indexed()
.map(|(i, _)| i)
.collect::<HashSet<usize>>();
@ -79,5 +119,5 @@ pub(crate) fn input(ui: &mut Ui, data: &mut MyApp) {
}
}
data.simulation.ants = ants;
}
}*/
}

View File

@ -2,6 +2,6 @@ mod data;
mod input;
mod graph;
pub(crate) use data::{MyApp, GlobalState};
pub(crate) use data::{MyApp, AntsVisualisationData};
pub(crate) use input::input;
pub(crate) use graph::graph_with_controls;

View File

@ -16,7 +16,7 @@ fn main() -> eframe::Result {
impl eframe::App for gui::MyApp {
fn update(&mut self, ui: &eframe::egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ui, |ui| match self.state {
gui::GlobalState::Edit {} => {
gui::AntsVisualisationData::Edit {} => {
match self.vertex_update {
UpdatePending::NoChange => {}
UpdatePending::Add => {
@ -32,13 +32,13 @@ impl eframe::App for gui::MyApp {
}
gui::input(ui, self)
}
gui::GlobalState::Running { .. } => {
gui::AntsVisualisationData::Running { .. } => {
ui.add_enabled_ui(false, |ui| gui::input(ui, self));
subwindow(
ui, "visualisation", "Visualisation",
|vb| vb.with_inner_size([640.0, 480.0]),
|ui| gui::graph_with_controls(ui, self),
).on_close(|| self.state = gui::GlobalState::Edit);
).on_close(|| self.state = gui::AntsVisualisationData::Edit);
}
});
}

View File

@ -14,9 +14,7 @@ impl<E> CompleteGraph<E> {
edges: EdgesVec::new(),
};
}
}
impl<E> CompleteGraph<E> {
pub fn add_vertex(&mut self, edge_constructor: impl Fn() -> E) {
self.vertices.add(|vertices, new_vi| {
let mut new_edges_set = HashSet::new();
@ -36,4 +34,7 @@ impl<E> CompleteGraph<E> {
}
self.vertices.remove(vi);
}
pub fn vertex_count(&self) -> usize {
return self.vertices.len();
}
}

View File

@ -0,0 +1,97 @@
use super::{
_ConstMutSwitchUiCallback, _ConstMutSwitchUiTableBaseCallback,
_ConstMutSwitchUiTableRowCallback, ConstMutSwitchUi, ConstRef, RefType,
};
use eframe::egui::{ScrollArea, Ui};
use eframe::emath::Numeric;
use std::ops::RangeInclusive;
pub struct ConstUI<'ui> {
ui: &'ui mut Ui,
}
impl<'ui> ConstUI<'ui> {
pub fn wrap(ui: &'ui mut Ui) -> Self {
return Self { ui };
}
}
impl ConstMutSwitchUi for ConstUI<'_> {
type RefType = ConstRef;
fn label(&mut self, s: &str) {
self.ui.label(s);
}
fn slider<T: Numeric>(
&mut self,
range: RangeInclusive<T>,
step: f64,
storage: &mut <Self::RefType as RefType>::Ref<'_, T>,
) {
let mut mirror = **storage;
self.ui.add_enabled_ui(false, |ui| {
crate::gui::slider(ui, &mut mirror, range, step);
});
}
fn labeled_slider<T: Numeric>(
&mut self,
name: &str,
range: RangeInclusive<T>,
step: f64,
storage: &mut &T,
) {
let mut mirror = **storage;
self.ui.add_enabled_ui(false, |ui| {
crate::gui::labeled_slider(ui, name, &mut mirror, range, step);
});
}
fn space(&mut self) {
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 button<T>(&mut self, label: &str, _storage: &mut &T, _on_click: impl FnOnce(&mut T)) {
self.ui.add_enabled_ui(false, |ui| ui.button(label));
}
fn separator(&mut self) {
self.ui.separator();
}
fn scroll_area_2<F: _ConstMutSwitchUiCallback<Self::RefType>>(
&mut self,
clojure: &mut F::Clojure<'_>,
) {
ScrollArea::both()
.auto_shrink([false, false])
.show_viewport(self.ui, |ui, _| F::render(&mut ConstUI { ui }, clojure));
}
fn table<
'a,
F: _ConstMutSwitchUiTableBaseCallback<ConstRef, Body = _B>,
_B: _ConstMutSwitchUiTableRowCallback<ConstRef, Clojure<'a> = _BC>,
_BC,
>(
&mut self,
vscroll: bool,
columns: usize,
header_clojure: <F::Header as _ConstMutSwitchUiTableRowCallback<ConstRef>>::Clojure<'_>,
body_data: impl Iterator<Item = _BC>,
) {
super::render_table::<ConstUI, F, Self>(
self.ui,
vscroll,
columns,
header_clojure,
body_data,
);
}
}

View File

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

View File

@ -0,0 +1,74 @@
use super::{ConstMutSwitchUi, MutRef, RefType, _ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback};
use crate::gui::const_mut_switch::r#trait::_ConstMutSwitchUiCallback;
use eframe::egui::{ScrollArea, Ui};
use eframe::emath::Numeric;
use std::ops::RangeInclusive;
pub struct MutUI<'ui> {
ui: &'ui mut Ui,
}
impl ConstMutSwitchUi for MutUI<'_> {
type RefType = MutRef;
fn label(&mut self, s: &str) {
self.ui.label(s);
}
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);
}
fn labeled_slider<T: Numeric>(
&mut self,
name: &str,
range: RangeInclusive<T>,
step: f64,
storage: &mut &mut T,
) {
crate::gui::labeled_slider(self.ui, name, *storage, range, step);
}
fn space(&mut self) {
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 button<T>(&mut self, label: &str, storage: &mut &mut T, on_click: impl FnOnce(&mut T)) {
if self.ui.button(label).clicked() {
on_click(*storage)
}
}
fn separator(&mut self) {
self.ui.separator();
}
fn scroll_area_2<F: _ConstMutSwitchUiCallback<Self::RefType>>(
&mut self,
clojure: &mut F::Clojure<'_>,
) {
ScrollArea::both()
.auto_shrink([false, false])
.show_viewport(self.ui, |ui, _| F::render(&mut MutUI { ui }, clojure));
}
fn table<
'a,
F: _ConstMutSwitchUiTableBaseCallback<MutRef, Body = _B>,
_B: _ConstMutSwitchUiTableRowCallback<MutRef, Clojure<'a> = _BC>,
_BC,
>(
&mut self,
vscroll: bool,
columns: usize,
header_clojure: <F::Header as _ConstMutSwitchUiTableRowCallback<MutRef>>::Clojure<'_>,
body_data: impl Iterator<Item = _BC>,
) {
super::render_table::<MutUI, F, Self>(self.ui, vscroll, columns, header_clojure, body_data);
}
}

View File

@ -0,0 +1,106 @@
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>;
fn _get<'r, 'f: 'r, 's: 'f, S, M>(
storge: &'f mut Self::Ref<'s, S>,
const_: impl FnOnce(&S) -> &M,
mut_: impl FnOnce(&mut S) -> &mut M,
) -> Self::Ref<'r, M>;
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 {}
impl RefType for ConstRef {
type Ref<'a, T: 'a> = &'a T;
fn _get<'r, 'f: 'r, 's: 'f, S, M>(
storge: &'f mut &'s S,
const_: impl FnOnce(&S) -> &M,
_mut: impl FnOnce(&mut S) -> &mut M,
) -> &'r M {
return const_(*storge);
}
unsafe fn _to_ptr<T>(r: &T) -> NonNull<T> {
return NonNull::from_ref(r);
}
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 {}
impl RefType for MutRef {
type Ref<'a, T: 'a> = &'a mut T;
fn _get<'r, 'f: 'r, 's: 'f, S, M>(
storge: &'f mut &'s mut S,
_const: impl FnOnce(&S) -> &M,
mut_: impl FnOnce(&mut S) -> &mut M,
) -> &'r mut M {
return mut_(*storge);
}
unsafe fn _to_ptr<T>(r: &mut T) -> NonNull<T> {
return NonNull::from_mut(r);
}
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

@ -0,0 +1,110 @@
use super::{_ConstMutSwitchUiCallback, ConstMutSwitchUi, ConstRef, ConstUI, MutRef, MutUI, RefType};
use eframe::egui::Ui;
use egui_extras::{Column, TableBuilder, TableRow};
use std::marker::PhantomData;
pub trait _ConstMutSwitchUiTableBaseCallback<RefType: super::RefType> {
type Header: _ConstMutSwitchUiTableRowCallback<RefType>;
type Body: _ConstMutSwitchUiTableRowCallback<RefType>;
fn header_to_body<'a>(
header: &mut <Self::Header as _ConstMutSwitchUiTableRowCallback<RefType>>::Clojure<'a>,
) -> impl Iterator<Item = <Self::Body as _ConstMutSwitchUiTableRowCallback<RefType>>::Clojure<'a>>;
}
pub trait _ConstMutSwitchUiTableRowCallback<RefType: super::RefType> {
type Clojure<'a>;
fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RefType>>(
row: &mut Row,
clojure: &mut Self::Clojure<'_>,
);
}
pub trait ConstMutSwitchUiTableRow {
type RefType: RefType;
fn cell<F: _ConstMutSwitchUiCallback<Self::RefType>>(&mut self, clojure: &mut F::Clojure<'_>);
}
pub(super) struct ConstMutSwitchUiTableRowImpl<
'a,
'b,
RefType: super::RefType,
Constructor: __SwitchUiConstructor1<RefType>,
> {
row: TableRow<'a, 'b>,
__phantom: PhantomData<(Constructor, RefType)>,
}
impl<RefType: super::RefType, Constructor: __SwitchUiConstructor1<RefType>> ConstMutSwitchUiTableRow
for ConstMutSwitchUiTableRowImpl<'_, '_, RefType, Constructor>
{
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));
}
}
trait __SwitchUiConstructor1<RefType: super::RefType> {
type Ctx<'a>: ConstMutSwitchUi<RefType = RefType>;
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a>;
}
impl __SwitchUiConstructor1<ConstRef> for ConstUI<'_> {
type Ctx<'a> = ConstUI<'a>;
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a> {
todo!()
}
}
impl __SwitchUiConstructor1<MutRef> for MutUI<'_> {
type Ctx<'a> = MutUI<'a>;
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a> {
todo!()
}
}
pub(super) fn render_table<
'a,
Ctx: ConstMutSwitchUi,
F: _ConstMutSwitchUiTableBaseCallback<Ctx::RefType>,
Constructor: __SwitchUiConstructor1<Ctx::RefType>,
>(
ui: &mut Ui,
vscroll: bool,
columnsCount: usize,
headerClojure: &mut <F::Header as _ConstMutSwitchUiTableRowCallback<Ctx::RefType>>::Clojure<'_>,
) {
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> {
row,
__phantom: PhantomData::default(),
},
headerClojure,
)
})
.body(|mut body| {
for mut rowData in F::header_to_body(headerClojure) {
body.row(20.0, |row| {
F::Body::render_row(
&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> {
row,
__phantom: PhantomData::default(),
},
&mut rowData,
)
});
}
});
}

View File

@ -0,0 +1,68 @@
use super::{
_ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback, ConstRef, RefType,
};
use eframe::emath::Numeric;
use std::ops::RangeInclusive;
pub trait ConstMutSwitchUi {
type RefType: RefType;
fn label(&mut self, s: &str);
fn slider<T: Numeric>(
&mut self,
range: RangeInclusive<T>,
step: f64,
storage: &mut <Self::RefType as RefType>::Ref<'_, T>,
);
fn labeled_slider<T: Numeric>(
&mut self,
name: &str,
range: RangeInclusive<T>,
step: f64,
storage: &mut <Self::RefType as RefType>::Ref<'_, T>,
);
fn space(&mut self);
fn horizontal<F: _ConstMutSwitchUiCallback<Self::RefType>>(
&mut self,
clojure: &mut F::Clojure<'_>,
);
fn button<T>(
&mut self,
label: &str,
storage: &mut <Self::RefType as RefType>::Ref<'_, T>,
on_click: impl FnOnce(&mut T),
);
fn separator(&mut self);
fn scroll_area_2<F: _ConstMutSwitchUiCallback<Self::RefType>>(
&mut self,
clojure: &mut F::Clojure<'_>,
);
fn table<
'a,
F: _ConstMutSwitchUiTableBaseCallback<Self::RefType>
>(
&mut self,
vscroll: bool,
columns: usize,
header_clojure: <F::Header as _ConstMutSwitchUiTableRowCallback<Self::RefType>>::Clojure<
'_,
>,
);
}
pub trait _ConstMutSwitchUiCallback<RefType: super::RefType> {
type Clojure<'a>;
fn render<SubCtx: ConstMutSwitchUi<RefType = RefType>>(
ctx: &mut SubCtx,
clojure: &mut Self::Clojure<'_>,
);
}

View File

@ -0,0 +1,686 @@
## Введение
Цель этого набора трейтов - описание графического интерфейса единым образом
(***ровно 1 раз***) независимо от возможности изменять данные, которые он визуализирует:
если свойство неизменяемое, соответствующий элемент отключается (`disabled="true"`); в
противном случае отображение происходит без дополнительных изменений.
Так как `&T` более ограниченный тип, чем `&mut T`, то весь функционал должен строиться
вокруг него. Но тогда возникают проблемы с тем, что нельзя преобразовать его в `&mut T`
без `unsafe` блоков.
Альтернативный вариант - параметризовать тип ссылок...
## 1
Начальный интерфейс содержал ассоциированный тип с самой ссылкой и методом
преобразования ссылки на тип в ссылку на её члена:
```rust
trait ConstMutSwitchUi {
type Ref<'a, T: 'a>;
fn _get<'r, 'f: 'r, 's: 'f, S, M>(
storge: &'f mut Self::Ref<'s, S>,
const_: impl FnOnce(&S) -> &M,
mut_: impl FnOnce(&mut S) -> &mut M,
) -> Self::Ref<'r, M>;
// ...
}
impl ConstMutSwitchUi for ConstUI<'_> {
type Ref<'a, T> = &'a T;
fn _get<'r, 'f: 'r, 's: 'f, S, M>(
storge: &'f mut &'s S,
const_: impl FnOnce(&S) -> &M,
_mut: impl FnOnce(&mut S) -> &mut M,
) -> &'r M {
return const_(*storge);
}
}
impl ConstMutSwitchUi for MutUI<'_> {
type RefType = &'a mut T;
fn _get<'r, 'f: 'r, 's: 'f, S, M>(
storge: &'f mut &'s mut S,
_const: impl FnOnce(&S) -> &M,
mut_: impl FnOnce(&mut S) -> &mut M,
) -> &'r mut M {
return mut_(*storge);
}
}
```
Ассоциированный тип `Ref<'a, T>` разворачивается в `&'a T` или `&'a mut T` в зависимости
от использованной реализации. Почему параметр `T` ограничен временем жизни `'a` - нет
идей, но компилятор заставил ограничить.
Расставить времена жизни в методе `_get` было довольно нетривиально (с учётом
малоинформативных сообщений об ошибках от компилятора):
* `'r` (*return*) - время жизни ссылки на члена типа;
* `'f` (*function*) - время жизни ссылки на ссылку;
* `'s` (*storage*) - время ссылки на значение, член которого нужно получить;
Лямбды без проблем работают и без явного указания времён жизни.
Параметр `storage` принимает ссылку на ссылку так как компилятор не знает о том, что `Ref`
это тоже ссылка, поэтому передача по значению приведёт к передаче владения внутрь метода
и недоступности ссылки после. Изменяемая ссылка нужна чтобы нельзя было получить
несколько изменяемых ссылок на членов одного значения. На практике обратное не тестировалось.
По аналогичной логике ссылки передавались в простые виджеты:
```rust
trait ConstMutSwitchUi {
// ...
fn labeled_slider<T: Numeric>(
&mut self,
name: &str,
range: RangeInclusive<T>,
step: f64,
storage: &mut Self::Ref<'_, T>,
);
// ...
}
```
## 2
### 2.1
Более сложные виджеты предоставляют локальные области и при создании обёртки нужно как-то
передавать тип ссылки в эти дочерние области. Самое очевидное решение использовать лямбду:
которая принимает `impl ConstMutSwitchUi` в качестве параметра, но так как тип функции сам
по себе трейт, а компилятор такое не поддерживает, такой вариант не подходит.
Для обхода этого ограничения создан дополнительный функциональный трейт:
```rust
trait _ConstMutSwitchUiCallback {
type Clojure;
fn render<SubCtx: ConstMutSwitchUi>(
&self,
ctx: &mut SubCtx,
clojure: &mut Self::Clojure,
);
}
```
Параметр `clojure` является изменяемой ссылкой по той же причине, что и раньше.
Этот трейт имплементируется пользователем на каком-нибудь анонимном типе и передаётся в
метод рантайма:
```rust
trait ConstMutSwitchUi {
// ...
fn horizontal<F: _ConstMutSwitchUiCallback>(
&mut self,
clojure: &mut F::Clojure,
scope: F
);
// ...
}
```
### 2.2
Следующая проблема - перевести ссылки из родительского `Ref` в дочерний `Ref`. Попытки
сделать трейт для перевода ссылок оказались безуспешными, так как в переменное число
шаблонных параметров, из-за чего нельзя переводить значения, содержащие более одной
ссылки: `(&T1, &T2)`.
```rust
pub trait RefsMapper<Ctx: ConstMutSwitchUi> {
type NewCtx<'n>: ConstMutSwitchUi;
fn _map_ctx<'n>(&self) -> impl Refs<Self::NewCtx<'n>>;
}
```
Возможно у этого способа существовали и другие проблемы, но он не дожил до их выявления.
Другой подход - сказать тип ссылки у дочерней области такой же как и у родительской, но
тогда возникает проблема невозможности использовать локальные шаблонные параметры
при подстановке ассоциированного типа:
```rust
trait ConstMutSwitchUi {
// ...
fn horizontal<F: _ConstMutSwitchUiCallback<Ref<'a, T>=Self::Ref<'a, T>>>(
&mut self,
clojure: &mut F::Clojure,
scope: F
);
// ...
}
```
Но зато можно создать ещё один трейт:
```rust
pub trait RefType {
type Ref<'a, T: 'a>;
}
impl RefType for ConstRef {
type Ref<'a, T: 'a> = &'a T;
}
impl RefType for MutRef {
type Ref<'a, T: 'a> = &'a mut T;
}
trait ConstMutSwitchUi {
type RefType: RefType;
// ...
fn horizontal<F: _ConstMutSwitchUiCallback<Self::RefType>>(
&mut self,
clojure: &mut F::Clojure,
scope: F
);
// ...
}
trait _ConstMutSwitchUiCallback<RefType: RefType> {
type Clojure;
fn render<SubCtx: ConstMutSwitchUi<RefType=Self::RefType>>(
&self,
ctx: &mut SubCtx,
clojure: &mut Self::Clojure,
);
}
```
Это решает проблему идентичности типов ссылок, но вынуждает писать чуть более громоздкий
код (`Ctx::RefType::RefType` или `<Ctx::RefType as RefType>::Ref` вместо `Ctx::Ref`).
```rust
trait ConstMutSwitchUi {
type RefType: RefType;
// ...
fn horizontal<F: _ConstMutSwitchUiCallback<Self::RefType>>(
&mut self,
clojure: &mut F::Clojure,
scope: F
);
// ...
}
trait _ConstMutSwitchUiCallback<RefType: RefType> {
type Clojure;
fn render<SubCtx: ConstMutSwitchUi<RefType=Self::RefType>>(
&self,
ctx: &mut SubCtx,
clojure: &mut Self::Clojure,
);
}
```
### 2.3
Так как все необходимые данные передаются через параметры методов, необходимость в `&self`
отпала. Это избавляет от необходимости прописывать всех типы и имена в самой структуре и
потенциально упрощает код:
```rust
trait ConstMutSwitchUi {
// ...
fn horizontal<F: _ConstMutSwitchUiCallback<Ref<'a, T>=Self::Ref<'a, T>>>(
&mut self,
clojure: &mut F::Clojure,
);
// ...
}
trait _ConstMutSwitchUiCallback<RefType: RefType> {
type Clojure;
fn render<SubCtx: ConstMutSwitchUi<RefType=Self::RefType>>(
ctx: &mut SubCtx,
clojure: &mut Self::Clojure,
);
}
```
### 2.4
Предпринимались попытки сделать обращение к типу ссылки более лаконичным через
дополнительный трейт:
```rust
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>;
}
```
Но в таком случае компилятор не понимает, что `ConstMutSwitchUi::RefType::RefType` и
`RefGetter::Ref`.
Ручная реализация трейта `Fn` для `_ConstMutSwitchUiCallback` является экспериментальной,
поэтому реализована не была.
## 3
### 3.1
Вишенка на торте - создание таблицы. Аналогично набор из большого количества трейтов,
половина из которых могли бы быть заменены лямбдами. Первая версия:
```rust
trait _ConstMutSwitchUiTableBaseCallback<RefType: super::RefType> {
type Clojure;
fn render_table<Root: ConstMutSwitchUiTableBase<RefType = RefType>>(
clojure: &mut Self::Clojure,
);
}
trait ConstMutSwitchUiTableBase {
type RefType: RefType;
fn header<F: _ConstMutSwitchUiTableRowCallback<Self::RefType, ()>>(
&mut self,
clojure: &mut F::Clojure,
);
fn body<F: _ConstMutSwitchUiTableRowCallback<Self::RefType, usize>>(
&mut self,
clojure: &mut F::Clojure,
);
}
trait _ConstMutSwitchUiTableRowCallback<RefType: RefType, I> {
type Clojure;
fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RefType>>(
row: &mut Row,
index: I,
clojure: &mut Self::Clojure,
);
}
trait ConstMutSwitchUiTableRow {
type RefType: RefType;
fn cell<F: _ConstMutSwitchUiCallback<Self::RefType>>(&mut self, clojure: &mut F::Clojure);
}
```
```rust
struct ConstMutSwitchUiTableRowImpl<
'a,
'b,
'c,
Ctx: ConstMutSwitchUi,
Constructor: Fn(&mut Ui) -> Ctx,
> {
row: TableRow<'a, 'b>,
constructor: &'c Constructor,
}
impl<Ctx: ConstMutSwitchUi, Constructor: Fn(&mut Ui) -> Ctx> ConstMutSwitchUiTableRow
for ConstMutSwitchUiTableRowImpl<'_, '_, '_, Ctx, Constructor>
{
type RefType = Ctx::RefType;
fn cell<F: _ConstMutSwitchUiCallback<Self::RefType>>(&mut self, clojure: &mut F::Clojure) {
self.row
.col(|ui| F::render(&mut (self.constructor)(ui), clojure));
}
}
enum ConstMutSwitchUiTableBaseImplState<'a> {
Header(TableBuilder<'a>),
Body(Table<'a>),
Done,
}
impl<'a> ConstMutSwitchUiTableBaseImplState<'a> {
#[allow(deprecated)]
fn header_map(&mut self, f: impl FnOnce(TableBuilder) -> Table) {
match self {
ConstMutSwitchUiTableBaseImplState::Header(_) => {
let mut local = unsafe { Self::Header(uninitialized()) };
swap(self, &mut local);
if let Self::Header(local) = local {
*self = Self::Body(f(local))
}
}
_ => panic!("Not in header state"),
}
}
#[allow(deprecated)]
fn body_map(&mut self, f: impl FnOnce(Table) ) {
match self {
ConstMutSwitchUiTableBaseImplState::Header(_) => {
let mut local = unsafe { Self::Body(uninitialized()) };
swap(self, &mut local);
if let Self::Body(local) = local {
f(local);
*self = Self::Done
}
}
_ => panic!("Not in body state"),
}
}
}
pub(super) struct ConstMutSwitchUiTableBaseImpl<
'a,
Ctx: ConstMutSwitchUi,
Constructor: Fn(&mut Ui) -> Ctx,
> {
table: ConstMutSwitchUiTableBaseImplState<'a>,
constructor: Constructor,
size: usize
}
impl<Ctx: ConstMutSwitchUi, Constructor: Fn(&mut Ui) -> Ctx> ConstMutSwitchUiTableBase
for ConstMutSwitchUiTableBaseImpl<'_, Ctx, Constructor>
{
type RefType = Ctx::RefType;
fn header<F: _ConstMutSwitchUiTableRowCallback<Self::RefType, ()>>(
&mut self,
clojure: &mut F::Clojure,
) {
match &mut self.table {
ConstMutSwitchUiTableBaseImplState::Body(_) | ConstMutSwitchUiTableBaseImplState::Done => {
panic!("Header duplication")
}
ConstMutSwitchUiTableBaseImplState::Header(h) => {
self.table.header_map(|h| {
return h.header(20.0, |row| {
F::render_row(
&mut ConstMutSwitchUiTableRowImpl {
row,
constructor: &self.constructor,
},
(),
clojure,
)
});
});
}
}
}
}
trait ConstMutSwitchUi {
// ...
fn table<F: _ConstMutSwitchUiTableBaseCallback<Self::RefType>>(
&mut self,
columns: usize,
rows: usize,
clojure: &mut F::Clojure,
);
// ...
}
```
### 3.2
Область `ConstMutSwitchUiTableBase` оказалась избыточной, так как каждый из методов будет
вызываться ровно один раз в фиксированном порядке. К тому же, из-за отсутствия возможности
писать конечные автоматы без "moving out of borrowed reference" приходится прибегать к
небезопасным трюкам. Упрощённая версия:
```rust
trait _ConstMutSwitchUiTableBaseCallback<RefType: super::RefType> {
type Header: _ConstMutSwitchUiTableRowCallback<RefType, ()>;
type Body: _ConstMutSwitchUiTableRowCallback<RefType, usize>;
}
trait ConstMutSwitchUi {
// ...
fn table<F: _ConstMutSwitchUiTableBaseCallback<Self::RefType>>(
&mut self,
vscroll: bool,
columns: usize,
header_clojure: &mut <F::Header as _ConstMutSwitchUiTableRowCallback<Self::RefType, ()>>::Clojure,
body_data: impl Iterator<Item=<F::Body as _ConstMutSwitchUiTableRowCallback<Self::RefType, usize>>::Clojure>,
);
// ...
}
```
### 3.3
Данные для строк передаются через итератор, поэтому нет необходимости в передаче номера строки
отдельно (если они нужны можно делать `it.enumerate()`):
```rust
trait _ConstMutSwitchUiTableBaseCallback<RefType: super::RefType> {
type Header: _ConstMutSwitchUiTableRowCallback<RefType>;
type Body: _ConstMutSwitchUiTableRowCallback<RefType>;
}
trait _ConstMutSwitchUiTableRowCallback<RefType: RefType> {
type Clojure;
fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RefType>>(
row: &mut Row,
clojure: &mut Self::Clojure,
);
}
trait ConstMutSwitchUi {
// ...
fn table<F: _ConstMutSwitchUiTableBaseCallback<Self::RefType>>(
&mut self,
vscroll: bool,
columns: usize,
header_clojure: &mut <F::Header as _ConstMutSwitchUiTableRowCallback<Self::RefType>>::Clojure,
body_data: impl Iterator<Item=<F::Body as _ConstMutSwitchUiTableRowCallback<Self::RefType>>::Clojure>,
);
// ...
}
```
### 3.4
При имплементации этого метода в структуре (просто от сигнатуры, с пустым телом) компилятор
сходит с ума и выдаёт ошибку о том, что *`F`doesn't implements
_ConstMutSwitchUiTableBaseCallback*. Решается эта проблема явной распаковкой всех ассоциированных
типов:
```rust
trait ConstMutSwitchUi {
// ...
fn table<
'a,
F: _ConstMutSwitchUiTableBaseCallback<Self::RefType, Body=_B>,
_B: _ConstMutSwitchUiTableRowCallback<Self::RefType, Clojure<'a>=_BC>,
_BC,
>(
&mut self,
vscroll: bool,
columns: usize,
header_clojure: <F::Header as _ConstMutSwitchUiTableRowCallback<Self::RefType>>::Clojure,
body_data: impl Iterator<Item = _BC>,
);
// ...
}
```
Что тут происходит с `'a` нет идей, но оно компилируется.
### 3.5
Что бы не писать повторять реализации контекста для таблиц (который довольно объёмный), код
был вынесен в функцию `table::render_table`:
```rust
pub(super) fn render_table<
Ctx: ConstMutSwitchUi,
F: _ConstMutSwitchUiTableBaseCallback<Ctx::RefType>,
>(
ui: &mut Ui,
vscroll: bool,
columnsCount: usize,
headerClojure: <F::Header as _ConstMutSwitchUiTableRowCallback<Ctx::RefType>>::Clojure,
rowsData: impl Iterator<
Item = <F::Body as _ConstMutSwitchUiTableRowCallback<Ctx::RefType>>::Clojure,
>,
constructor: impl Fn(&mut Ui) -> Ctx,
) {
// ...
}
```
Но при попытке создать конструктор всплывала проблема: компилятор не видит в сигнатуре лямбды,
что возвращаемое значение использует время жизни передаваемой ссылки, поэтому выдовал ошибку
об их несвязанности. Решение - просто ещё один трейт, который просто используется вместо
стандартного `Fn`:
```rust
trait __SwitchUiConstructor1<RefType: super::RefType> {
type Ctx<'a>: ConstMutSwitchUi<RefType = RefType>;
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a>;
}
```
### 3.6
Текущая реализация требует передачи двух изменяемых ссылок, что с точки зрения языка недопустимо.
Решение - передавать данные только для заголовка, а после его отрисовки превращать данные
в итератор для тела таблицы:
```rust
trait _ConstMutSwitchUiTableBaseCallback<RefType: super::RefType> {
type Header: _ConstMutSwitchUiTableRowCallback<RefType>;
type Body: _ConstMutSwitchUiTableRowCallback<RefType>;
fn header_to_body<'a>(
header: &mut <Self::Header as _ConstMutSwitchUiTableRowCallback<RefType>>::Clojure<'a>,
) -> impl Iterator<Item = <Self::Body as _ConstMutSwitchUiTableRowCallback<RefType>>::Clojure<'a>>;
}
trait _ConstMutSwitchUiTableRowCallback<RefType: super::RefType> {
type Clojure<'a>;
fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RefType>>(
row: &mut Row,
clojure: &mut Self::Clojure<'_>,
);
}
trait ConstMutSwitchUi {
// ...
fn table<
F: _ConstMutSwitchUiTableBaseCallback<Self::RefType>,
>(
&mut self,
vscroll: bool,
columns: usize,
header_clojure: &mut <F::Header as _ConstMutSwitchUiTableRowCallback<Self::RefType>>::Clojure
);
// ...
}
```
Метод отрисовки строки принимает ссылку, что бы была возможность потом передать
## 4
### 4.1
Для более прозрачной работы с типом ссылок им добавлено ограничение трейтом `Deref`, который
имплементируют оба типа ссылок и это не нарушает безопасность программы в обоих режимах.
```rust
trait RefType {
type Ref<'a, T: 'a>: Deref<Target=T>;
}
```
### 4.2
Метод `ConstMutSwitchUi::_get` нужен не только в функциях с `Ctx: ConstMutSwitchUi`, но и в
функциях `Ctx: ConstMutSwitchUiTableRow`, поэтому сначала была идея вынести её в общий
супертрейт, но это усложнило бы использование её в пользовательском коде, который обычно
указывает только `RefType`, поэтому метод и был перемещён именно туда:
```rust
trait RefType {
// ...
fn _get<'r, 'f: 'r, 's: 'f, S, M>(
storge: &'f mut Self::Ref<'s, S>,
const_: impl FnOnce(&S) -> &M,
mut_: impl FnOnce(&mut S) -> &mut M,
) -> Self::Ref<'r, M>;
// ...
}
```
Не в `Ref`, потому что это требует ещё одного трейта и будет вызывать коллизии с методом `_get`
у объекта по ссылке.
### 4.3
В некоторых случаях, чтобы победить borrow checker, нужно создавать весьма нетривиальные структуры
данных (которые всё равно будут внутри использовать `unsafe`). Структуры создавать сложно, скастовать
ссылку в сырой указатель - просто.
```rust
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>;
// ...
}
```
### 4.4
Итераторы, которые возвращают ссылки не могут быть корректно получены методом `RefType::_get`,
поэтому ещё один трейт:
```rust
trait RefType {
// ...
fn _iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>(
storge: &'f mut Self::Ref<'s, F::C>,
) -> impl Iterator<Item = &'r F::T<'r>>;
// ...
}
trait _IteratorGetter {
type T<'a>: 'a;
type C;
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>>;
}
```
Но способ работает только для очень простых случаев, так что малопригоден.

View File

@ -1,78 +1,173 @@
use crate::graph::{EdgesVec, VerticesVec};
use crate::gui::slider;
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 std::ptr::NonNull;
use crate::UpdatePending;
pub fn draw_lengths_table<D>(
ui: &mut Ui,
vertices: &mut VerticesVec,
edges: &mut EdgesVec<D>,
update: &mut UpdatePending,
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>,
) {
ScrollArea::both()
.auto_shrink([false, false])
.show_viewport(ui, |ui, _area| {
TableBuilder::new(ui)
.striped(true) // Alternating row colors
.resizable(true)
.vscroll(false)
.column(Column::remainder())
.columns(Column::remainder(), vertices.len())
.header(20.0, |mut header| {
header.col(|ui| {
ui.label("#");
});
for (i, _) in vertices.iter().enumerate() {
header.col(|ui| {
ui.horizontal(|ui| {
ui.label(i.to_string());
if ui.button("-").clicked() {
*update = UpdatePending::Remove(i);
}
});
});
}
})
.body(|mut body| {
for (ri, (vi, v)) in vertices.iter_indexed().enumerate() {
body.row(20.0, |mut row| {
row.col(|ui| {
ui.horizontal(|ui| {
ui.label(ri.to_string());
if ui.button("-").clicked() {
*update = UpdatePending::Remove(vi);
}
});
});
ctx.scroll_area_2::<ScrollArea>(&mut (graph, update));
let mut local_edges =
Vec::<(usize, NonNull<f64>)>::with_capacity(v.len());
for ei in v.iter() {
let e = &mut edges[*ei];
let p = (e.another(vi), &mut e.length);
local_edges.push((p.0, NonNull::from_mut(p.1)));
}
local_edges.sort_by_key(|(k, _)| *k);
struct ScrollArea {}
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for ScrollArea {
type Clojure<'a> = (RT::Ref<'a, CompleteGraph<()>>, RT::Ref<'a, UpdatePending>);
let mut ci = 0usize;
for &(_, mut l) in local_edges.iter() {
if (ci == ri) {
row.col(|ui| {});
ci += 1
}
row.col(|ui| {
slider(ui, unsafe { l.as_mut() }, 0.0..=10.0, 0.1);
});
ci += 1;
}
if ri == vertices.len() - 1 {
row.col(|ui| {});
}
});
}
});
});
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: (),
})
}
}
}
struct Table{}
impl <RT: RefType>_ConstMutSwitchUiTableBaseCallback<RT> for Table {
type Header = Header;
type Body = Body;
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>;
fn get_const_iterator<'a>(cont: &'a Self::C) -> impl Iterator<Item=&Self::T<'a>> {
return cont.iter_indexed()
}
fn get_mut_iterator<'a>(cont: &'a mut Self::C) -> impl Iterator<Item=&mut Self::T<'a>> {
return cont.iter_indexed_mut()
}
}
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,
}
})
}
}
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<()>>,
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>;
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 ()) {}
}
}
}
struct Title {}
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for Title {
type Clojure<'a> = (usize, NonNull<RT::Ref<'a, 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)
})
}
}

View File

@ -1,10 +1,11 @@
mod boot;
pub mod const_mut_switch;
pub mod lengths_table;
mod render_graph;
mod slider;
mod subwindow;
pub use boot::boot_eframe;
pub use render_graph::render_graph;
pub use slider::{labeled_slider, slider};
pub use subwindow::subwindow;
pub use render_graph::render_graph;