use egui::{Id, Pos2, Rect, Ui, Vec2};
use petgraph::{stable_graph::IndexType, EdgeType};
use serde::{Deserialize, Serialize};
use crate::{node_size, DisplayNode, Node};
const KEY_PREFIX: &str = "egui_graphs_metadata";
#[derive(Clone, Debug, Serialize, Deserialize)]
struct Bounds {
min: Pos2,
max: Pos2,
}
impl Default for Bounds {
fn default() -> Self {
Self {
min: Pos2::new(f32::MAX, f32::MAX),
max: Pos2::new(f32::MIN, f32::MIN),
}
}
}
impl Bounds {
pub fn compute_next<
N: Clone,
E: Clone,
Ty: EdgeType,
Ix: IndexType,
D: DisplayNode<N, E, Ty, Ix>,
>(
&mut self,
n: &Node<N, E, Ty, Ix, D>,
) {
let size = node_size(n, Vec2::new(0., 1.));
let loc = n.location();
if loc.x - size < self.min.x {
self.min.x = loc.x - size;
}
if loc.x + size > self.max.x {
self.max.x = loc.x + size;
}
if loc.y - size < self.min.y {
self.min.y = loc.y - size;
}
if loc.y + size > self.max.y {
self.max.y = loc.y + size;
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MetadataFrame {
pub zoom: f32,
pub pan: Vec2,
pub last_step_time_ms: f32,
pub last_draw_time_ms: f32,
id: String,
bounds: Bounds,
}
impl Default for MetadataFrame {
fn default() -> Self {
Self {
zoom: 1.,
pan: Vec2::default(),
last_step_time_ms: 0.0,
last_draw_time_ms: 0.0,
bounds: Bounds::default(),
id: "".to_string(),
}
}
}
impl MetadataFrame {
pub fn new(id: Option<String>) -> Self {
Self {
id: id.unwrap_or_default(),
..Default::default()
}
}
pub fn load(self, ui: &egui::Ui) -> Self {
ui.data_mut(|data| {
data.get_persisted::<MetadataFrame>(Id::new(self.get_id()))
.unwrap_or(self.clone())
})
}
pub fn save(self, ui: &mut egui::Ui) {
ui.data_mut(|data| {
data.insert_persisted(Id::new(self.get_id()), self);
});
}
pub fn canvas_to_screen_pos(&self, pos: Pos2) -> Pos2 {
(pos.to_vec2() * self.zoom + self.pan).to_pos2()
}
pub fn canvas_to_screen_size(&self, size: f32) -> f32 {
size * self.zoom
}
pub fn screen_to_canvas_pos(&self, pos: Pos2) -> Pos2 {
((pos.to_vec2() - self.pan) / self.zoom).to_pos2()
}
pub fn process_bounds<
N: Clone,
E: Clone,
Ty: EdgeType,
Ix: IndexType,
D: DisplayNode<N, E, Ty, Ix>,
>(
&mut self,
n: &Node<N, E, Ty, Ix, D>,
) {
self.bounds.compute_next(n);
}
pub fn expand_bounds(&mut self, min: Pos2, max: Pos2) {
if min.x < self.bounds.min.x {
self.bounds.min.x = min.x;
}
if min.y < self.bounds.min.y {
self.bounds.min.y = min.y;
}
if max.x > self.bounds.max.x {
self.bounds.max.x = max.x;
}
if max.y > self.bounds.max.y {
self.bounds.max.y = max.y;
}
}
pub fn graph_bounds(&self) -> Rect {
Rect::from_min_max(self.bounds.min, self.bounds.max)
}
pub fn reset_bounds(&mut self) {
self.bounds = Bounds::default();
}
pub fn get_id(&self) -> Id {
egui::Id::new(format!("{KEY_PREFIX}_{}", self.id.clone()))
}
}
pub fn instance_scoped_id(widget_id: Id, custom_id: Option<String>, suffix: &'static str) -> Id {
widget_id.with((KEY_PREFIX, custom_id.unwrap_or_default(), suffix))
}
pub fn reset_metadata(ui: &mut Ui, id: Option<String>) {
MetadataFrame::new(id).save(ui);
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MetadataInstance {
pub last_top_left: Pos2,
pub first_frame_pending: bool,
}
impl MetadataInstance {
pub fn load(
ui: &mut Ui,
widget_id: Id,
custom_id: &Option<String>,
fallback_top_left: Pos2,
) -> Self {
let key = instance_scoped_id(widget_id, custom_id.clone(), "local");
ui.ctx().data_mut(|data| {
data.get_persisted::<MetadataInstance>(key)
.unwrap_or(MetadataInstance {
last_top_left: fallback_top_left,
first_frame_pending: true,
})
})
}
pub fn save(&self, ui: &mut Ui, widget_id: Id, custom_id: &Option<String>) {
let key = instance_scoped_id(widget_id, custom_id.clone(), "local");
ui.ctx().data_mut(|data| {
data.insert_persisted(key, self.clone());
});
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MetadataSync {
pub drag_owner: Option<String>,
pub hover_owner: Option<String>,
}
impl MetadataSync {
pub fn load(ui: &mut Ui, custom_id: &Option<String>) -> Self {
let drag_owner = ui.ctx().data_mut(|data| {
data.get_persisted::<Option<String>>(shared_instance_id(
custom_id.clone(),
"drag_owner",
))
.unwrap_or(None)
});
let hover_owner = ui.ctx().data_mut(|data| {
data.get_persisted::<Option<String>>(shared_instance_id(
custom_id.clone(),
"hover_owner",
))
.unwrap_or(None)
});
Self {
drag_owner,
hover_owner,
}
}
pub fn save(&self, ui: &mut Ui, custom_id: &Option<String>) {
ui.ctx().data_mut(|data| {
data.insert_persisted(
shared_instance_id(custom_id.clone(), "drag_owner"),
self.drag_owner.clone(),
);
data.insert_persisted(
shared_instance_id(custom_id.clone(), "hover_owner"),
self.hover_owner.clone(),
);
});
}
}
pub fn shared_instance_id(custom_id: Option<String>, suffix: &'static str) -> Id {
Id::new(format!(
"{KEY_PREFIX}_{}_{}",
custom_id.unwrap_or_default(),
suffix
))
}
pub fn instance_key_string(
widget_id: Id,
custom_id: Option<String>,
suffix: &'static str,
) -> String {
format!(
"{KEY_PREFIX}/{}/#{:?}/{}",
custom_id.unwrap_or_default(),
widget_id,
suffix
)
}