Attempt 1 to make mutability-independent UI
This commit is contained in:
parent
088cf8d194
commit
f295dc7a12
@ -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;
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@ -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;
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
97
utility/src/gui/const_mut_switch/const.rs
Normal file
97
utility/src/gui/const_mut_switch/const.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
14
utility/src/gui/const_mut_switch/mod.rs
Normal file
14
utility/src/gui/const_mut_switch/mod.rs
Normal 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};
|
||||
74
utility/src/gui/const_mut_switch/mut.rs
Normal file
74
utility/src/gui/const_mut_switch/mut.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
106
utility/src/gui/const_mut_switch/ref.rs
Normal file
106
utility/src/gui/const_mut_switch/ref.rs
Normal 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>;
|
||||
}
|
||||
110
utility/src/gui/const_mut_switch/table.rs
Normal file
110
utility/src/gui/const_mut_switch/table.rs
Normal 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,
|
||||
)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
68
utility/src/gui/const_mut_switch/trait.rs
Normal file
68
utility/src/gui/const_mut_switch/trait.rs
Normal 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<'_>,
|
||||
);
|
||||
}
|
||||
686
utility/src/gui/const_mut_switch/эволюция_костылей.md
Normal file
686
utility/src/gui/const_mut_switch/эволюция_костылей.md
Normal 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>>;
|
||||
}
|
||||
|
||||
```
|
||||
Но способ работает только для очень простых случаев, так что малопригоден.
|
||||
@ -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);
|
||||
ctx.scroll_area_2::<ScrollArea>(&mut (graph, update));
|
||||
|
||||
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: (),
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
})
|
||||
.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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
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(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)));
|
||||
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);
|
||||
|
||||
let mut ci = 0usize;
|
||||
for &(_, mut l) in local_edges.iter() {
|
||||
if (ci == ri) {
|
||||
row.col(|ui| {});
|
||||
ci += 1
|
||||
for (column_index, cell_data) in local_edges.iter_mut().enumerate() {
|
||||
if (column_index == clojure.row_id) {
|
||||
row.cell::<EmptyCell>(&mut ());
|
||||
}
|
||||
row.col(|ui| {
|
||||
slider(ui, unsafe { l.as_mut() }, 0.0..=10.0, 0.1);
|
||||
});
|
||||
ci += 1;
|
||||
row.cell::<Cell>(&mut unsafe { RT::_from_ptr(cell_data.1) });
|
||||
}
|
||||
|
||||
if ri == vertices.len() - 1 {
|
||||
row.col(|ui| {});
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
Loading…
Reference in New Issue
Block a user