use egui::{epaint::PathStroke, Color32, LayerId, Shape, Stroke};
use crate::ConnectionInProgress;
use super::{stages, GraphEditor, GraphResponse, RenderedSocket};
impl<S> GraphEditor<stages::Connections<S>> {
#[inline]
pub fn show_connections(self, build_fn: impl FnOnce(&mut ConnectionsUi<S>)) -> GraphResponse<S>
where
S: Send + Sync + Clone + 'static,
{
let Self {
id,
stage:
stages::Connections {
ui,
state,
response,
viewport,
sockets,
socket_interaction,
},
} = self;
let (connection, in_progress) = match socket_interaction {
crate::socket::SocketInteraction::None => (None, None),
crate::socket::SocketInteraction::Connect(a, b) => (Some((a, b)), None),
crate::socket::SocketInteraction::InProgress(in_progress) => (None, Some(in_progress)),
};
let layer_id = LayerId::new(egui::Order::Background, id);
let mut painter = ui.painter().clone();
painter.set_layer_id(layer_id);
let mut connections_ui = ConnectionsUi {
preferred_color: ui.visuals().strong_text_color(),
painter,
sockets,
connection: in_progress,
};
build_fn(&mut connections_ui);
connections_ui
.in_progress_connection_line(Stroke::new(5.0, connections_ui.preferred_color()));
let ConnectionsUi {
preferred_color: _,
painter: _,
sockets,
connection: _,
} = connections_ui;
let position = viewport.grid.canvas_to_graph(state.viewport_position);
state.store(ui.ctx(), id);
GraphResponse {
viewport,
response,
sockets,
connection,
position,
}
}
}
pub struct ConnectionsUi<S> {
preferred_color: Color32,
painter: egui::Painter,
sockets: Vec<RenderedSocket<S>>,
connection: Option<ConnectionInProgress<S>>,
}
impl<S> ConnectionsUi<S> {
#[inline]
pub fn preferred_color(&self) -> Color32 {
self.preferred_color
}
}
impl<S> ConnectionsUi<S> {
#[inline]
pub fn in_progress_connection(
&mut self,
show: impl FnOnce(&egui::Painter, ConnectionInProgress<S>),
) {
if let Some(connection) = self.connection.take() {
let mut top_most_painter = self.painter.clone();
top_most_painter.set_layer_id(LayerId::new(
egui::Order::Tooltip,
self.painter.layer_id().id,
));
show(&top_most_painter, connection);
}
}
#[inline]
pub fn in_progress_connection_line(&mut self, stroke: impl Into<Stroke>) {
self.in_progress_connection(|painter, connection| {
let ConnectionInProgress {
source,
target: _,
pointer_pos,
} = connection;
let points = [source.pos(), pointer_pos];
let stroke = stroke.into();
painter.add(Shape::LineSegment { points, stroke });
});
}
#[inline]
pub fn in_progress_connection_line_with_feedback(
&mut self,
stroke: impl FnOnce(RenderedSocket<S>, Option<RenderedSocket<S>>) -> Stroke,
) {
self.in_progress_connection(|painter, connection| {
let ConnectionInProgress {
source,
target,
pointer_pos,
} = connection;
let points = [source.pos(), pointer_pos];
let stroke = stroke(source, target);
painter.add(Shape::LineSegment { points, stroke });
});
}
}
impl<S> ConnectionsUi<S>
where
S: PartialEq,
{
#[inline]
pub fn connect_with(
&mut self,
a: &S,
b: &S,
show: impl FnOnce(&egui::Painter, &RenderedSocket<S>, &RenderedSocket<S>),
) {
if let Some(a) = self.sockets.iter().find(|s| &s.id == a) {
if let Some(b) = self.sockets.iter().find(|s| &s.id == b) {
show(&self.painter, a, b);
}
}
}
#[inline]
pub fn connect_line(&mut self, a: &S, b: &S, stroke: impl Into<Stroke>) {
self.connect_with(a, b, |painter, a, b| {
let stroke = stroke.into();
painter.add(Shape::LineSegment {
points: [a.pos(), b.pos()],
stroke,
});
});
}
#[inline]
pub fn connect_bezier(&mut self, a: &S, b: &S, stroke: impl Into<PathStroke>) {
self.connect_with(a, b, |painter, a, b| {
let a_pos = a.pos();
let b_pos = b.pos();
let control_scale = (a_pos.x - b_pos.x).abs() * 1.0 / 2.0;
let a_control = match a.side {
crate::NodeSide::Left => -control_scale,
crate::NodeSide::Right => control_scale,
};
let b_control = match b.side {
crate::NodeSide::Left => -control_scale,
crate::NodeSide::Right => control_scale,
};
let a_control = egui::Pos2 {
x: a_pos.x + a_control,
y: a_pos.y,
};
let b_control = egui::Pos2 {
x: b_pos.x + b_control,
y: b_pos.y,
};
let bezier = egui::epaint::CubicBezierShape::from_points_stroke(
[a_pos, a_control, b_control, b_pos],
false,
Color32::TRANSPARENT,
stroke,
);
painter.add(bezier);
});
}
}