Compare commits
1 commit
master
...
demo-node-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9b0de108c8 |
6 changed files with 226 additions and 0 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -661,6 +661,7 @@ dependencies = [
|
||||||
"epi",
|
"epi",
|
||||||
"image",
|
"image",
|
||||||
"serde",
|
"serde",
|
||||||
|
"slotmap",
|
||||||
"syntect",
|
"syntect",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1812,6 +1813,16 @@ version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slotmap"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab3003725ae562cf995f3dc82bb99e70926e09000396816765bb6d7adbe740b1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
|
|
|
@ -48,6 +48,17 @@ pub fn reset_button<T: Default + PartialEq>(ui: &mut Ui, value: &mut T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Show a button to reset a value to its default.
|
||||||
|
/// The button is only enabled if the value does not already have its original value.
|
||||||
|
pub fn reset_button_with<T: Default + PartialEq>(ui: &mut Ui, value: &mut T, def: T) {
|
||||||
|
if ui
|
||||||
|
.add(Button::new("Reset").enabled(*value != def))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
*value = def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
pub fn stroke_ui(ui: &mut crate::Ui, stroke: &mut epaint::Stroke, text: &str) {
|
pub fn stroke_ui(ui: &mut crate::Ui, stroke: &mut epaint::Stroke, text: &str) {
|
||||||
|
|
|
@ -22,6 +22,10 @@ epi = { version = "0.9.0", path = "../epi" }
|
||||||
image = { version = "0.23", default_features = false, features = ["jpeg", "png"], optional = true }
|
image = { version = "0.23", default_features = false, features = ["jpeg", "png"], optional = true }
|
||||||
syntect = { version = "4", default_features = false, features = ["default-fancy"], optional = true } # optional syntax highlighting
|
syntect = { version = "4", default_features = false, features = ["default-fancy"], optional = true } # optional syntax highlighting
|
||||||
|
|
||||||
|
# feature "node_graph":
|
||||||
|
slotmap = { version = "1", default_features = false, features = ["serde"] } # TODO: optional
|
||||||
|
# petgraph = { version = "0.5", default_features = false, features = [] } # TODO: optional
|
||||||
|
|
||||||
# feature "persistence":
|
# feature "persistence":
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ mod easy_mark_editor;
|
||||||
mod fractal_clock;
|
mod fractal_clock;
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
mod http_app;
|
mod http_app;
|
||||||
|
pub mod node_graph;
|
||||||
|
|
||||||
pub use color_test::ColorTest;
|
pub use color_test::ColorTest;
|
||||||
pub use demo::DemoApp;
|
pub use demo::DemoApp;
|
||||||
|
|
191
egui_demo_lib/src/apps/node_graph.rs
Normal file
191
egui_demo_lib/src/apps/node_graph.rs
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
use egui::{containers::*, *};
|
||||||
|
|
||||||
|
// pub use petgraph::graph::{EdgeIndex as EdgeId, NodeIndex as NodeId};
|
||||||
|
slotmap::new_key_type! { pub struct EdgeId; }
|
||||||
|
slotmap::new_key_type! { pub struct NodeId; }
|
||||||
|
|
||||||
|
pub type Nodes = slotmap::SlotMap<NodeId, Node>;
|
||||||
|
pub type Edges = slotmap::SlotMap<EdgeId, Edge>;
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
|
pub struct NodeGraph {
|
||||||
|
// graph: petgraph::Graph<Node, Edge>,
|
||||||
|
nodes: Nodes,
|
||||||
|
edges: Edges,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct Edge {
|
||||||
|
nodes: [NodeId; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Edge {
|
||||||
|
pub fn new(from: NodeId, to: NodeId) -> Self {
|
||||||
|
Self { nodes: [from, to] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct Node {
|
||||||
|
/// PERSISTED MODE: position is relative to parent!
|
||||||
|
rect: Rect,
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Node {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
rect: Rect::from_center_size(Pos2::ZERO, Vec2::splat(200.0)),
|
||||||
|
title: "unnamed".to_owned(),
|
||||||
|
description: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node {
|
||||||
|
pub fn new(title: impl Into<String>, pos: impl Into<Pos2>) -> Self {
|
||||||
|
Self {
|
||||||
|
title: title.into(),
|
||||||
|
rect: Rect::from_center_size(pos.into(), Vec2::splat(100.0)),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn window(&mut self, id: NodeId, ui: &mut Ui) {
|
||||||
|
let translation = ui.min_rect().min - Pos2::ZERO;
|
||||||
|
|
||||||
|
// TODO: set to use same layer as parent `ui`
|
||||||
|
let response = Window::new(self.title.clone())
|
||||||
|
.id(egui::Id::new(id))
|
||||||
|
.collapsible(false)
|
||||||
|
.scroll(false)
|
||||||
|
.resizable(true)
|
||||||
|
.default_size(self.rect.size())
|
||||||
|
.title_bar(false)
|
||||||
|
.current_pos(self.rect.min + translation)
|
||||||
|
//.show_inside(ui, |ui|{ // TODO
|
||||||
|
.show(ui.ctx(), |ui| {
|
||||||
|
self.ui(ui);
|
||||||
|
});
|
||||||
|
let response = response.expect("Window can't be closed");
|
||||||
|
self.rect = response.rect.translate(-translation);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ui(&mut self, ui: &mut Ui) {
|
||||||
|
let Self {
|
||||||
|
rect: _,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
// Manual titlebar with editable title:
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
|
ui.add(
|
||||||
|
TextEdit::singleline(title)
|
||||||
|
.desired_width(32.0)
|
||||||
|
.text_style(TextStyle::Heading)
|
||||||
|
.frame(false),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
// Body:
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Description:");
|
||||||
|
ui.text_edit_singleline(description);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Edge {
|
||||||
|
fn ui(&self, ui: &Ui, nodes: &Nodes) {
|
||||||
|
let translation = ui.min_rect().min - Pos2::ZERO;
|
||||||
|
|
||||||
|
if let (Some(node0), Some(node1)) = (nodes.get(self.nodes[0]), nodes.get(self.nodes[1])) {
|
||||||
|
let rects = [
|
||||||
|
node0.rect.translate(translation),
|
||||||
|
node1.rect.translate(translation),
|
||||||
|
];
|
||||||
|
|
||||||
|
let x = lerp(rects[0].center().x..=rects[1].center().x, 0.5);
|
||||||
|
let y = lerp(rects[0].center().y..=rects[1].center().y, 0.5);
|
||||||
|
|
||||||
|
let p0 = rects[0].clamp(pos2(x, y));
|
||||||
|
let p1 = rects[1].clamp(pos2(x, y));
|
||||||
|
|
||||||
|
let stroke = Stroke::new(2.0, Color32::from_gray(200));
|
||||||
|
ui.painter().arrow(p0, p1 - p0, stroke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeGraph {
|
||||||
|
fn egiu_deps() -> Self {
|
||||||
|
let mut graph = Self::default();
|
||||||
|
let emath = graph.add_node(Node::new("emath", [200., 500.]));
|
||||||
|
let epaint = graph.add_node(Node::new("epaint", [200., 400.]));
|
||||||
|
let egui = graph.add_node(Node::new("egui", [200., 300.]));
|
||||||
|
|
||||||
|
graph.add_edge(Edge::new(egui, emath));
|
||||||
|
graph.add_edge(Edge::new(egui, epaint));
|
||||||
|
|
||||||
|
graph
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_node(&mut self, node: Node) -> NodeId {
|
||||||
|
self.nodes.insert(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_edge(&mut self, edge: Edge) -> EdgeId {
|
||||||
|
self.edges.insert(edge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl epi::App for NodeGraph {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"Node Graph"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
|
fn load(&mut self, storage: &dyn epi::Storage) {
|
||||||
|
*self = epi::get_value(storage, "egui_demo_lib/apps/node_graph").unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "persistence")]
|
||||||
|
fn save(&mut self, storage: &mut dyn epi::Storage) {
|
||||||
|
epi::set_value(storage, "egui_demo_lib/apps/node_graph", self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, ctx: &egui::CtxRef, _frame: &mut epi::Frame<'_>) {
|
||||||
|
// TODO: side panel with "add windows" and whatnot
|
||||||
|
|
||||||
|
egui::SidePanel::left("control_ui", 100.0).show(ctx, |ui| self.control_ui(ui));
|
||||||
|
|
||||||
|
egui::CentralPanel::default()
|
||||||
|
.frame(Frame::dark_canvas(&ctx.style()))
|
||||||
|
.show(ctx, |ui| self.contents_ui(ui));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeGraph {
|
||||||
|
pub fn control_ui(&mut self, ui: &mut Ui) {
|
||||||
|
// egui::reset_button_with(ui, self, Self::egiu_deps());
|
||||||
|
if ui.button("Reset").clicked() {
|
||||||
|
*self = Self::egiu_deps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contents_ui(&mut self, ui: &mut Ui) {
|
||||||
|
for (node_id, node) in &mut self.nodes {
|
||||||
|
node.window(node_id, ui);
|
||||||
|
}
|
||||||
|
for (_edge_id, edge) in &mut self.edges {
|
||||||
|
edge.ui(ui, &mut self.nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ pub struct Apps {
|
||||||
http: crate::apps::HttpApp,
|
http: crate::apps::HttpApp,
|
||||||
clock: crate::apps::FractalClock,
|
clock: crate::apps::FractalClock,
|
||||||
color_test: crate::apps::ColorTest,
|
color_test: crate::apps::ColorTest,
|
||||||
|
node_graph: crate::apps::node_graph::NodeGraph,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Apps {
|
impl Apps {
|
||||||
|
@ -20,6 +21,7 @@ impl Apps {
|
||||||
("http", &mut self.http as &mut dyn epi::App),
|
("http", &mut self.http as &mut dyn epi::App),
|
||||||
("clock", &mut self.clock as &mut dyn epi::App),
|
("clock", &mut self.clock as &mut dyn epi::App),
|
||||||
("colors", &mut self.color_test as &mut dyn epi::App),
|
("colors", &mut self.color_test as &mut dyn epi::App),
|
||||||
|
("node_graph", &mut self.node_graph as &mut dyn epi::App),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
}
|
}
|
||||||
|
@ -43,11 +45,17 @@ impl epi::App for WrapApp {
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
fn load(&mut self, storage: &dyn epi::Storage) {
|
fn load(&mut self, storage: &dyn epi::Storage) {
|
||||||
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
||||||
|
// for (_, app) in self.iter_mut() { // less brittle!
|
||||||
|
// app.load(storage); // less brittle!
|
||||||
|
// } // less brittle!
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
fn save(&mut self, storage: &mut dyn epi::Storage) {
|
fn save(&mut self, storage: &mut dyn epi::Storage) {
|
||||||
epi::set_value(storage, epi::APP_KEY, self);
|
epi::set_value(storage, epi::APP_KEY, self);
|
||||||
|
// for (_, app) in self.iter_mut() {
|
||||||
|
// app.save(storage);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn warm_up_enabled(&self) -> bool {
|
fn warm_up_enabled(&self) -> bool {
|
||||||
|
|
Loading…
Reference in a new issue