use std::fmt::Debug;
use std::hash::Hash;
use egui::{vec2, Order, Pos2, Sense, Vec2, Widget};
use crate::{
cable::CableId, custom_widget::CustomWidget, default_plug::DefaultPlug, event::Event,
plug_params::PlugParams, prelude::PortId, state::State,
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum PlugType {
In,
Out,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct PlugId {
cable_id: CableId,
plug_type: PlugType,
}
impl PlugId {
pub fn new(cable_id: CableId, plug_type: PlugType) -> Self {
PlugId {
cable_id,
plug_type,
}
}
}
#[derive(Debug, Default)]
pub struct Plug {
pub plug_to: Option<PortId>,
pos: Option<Pos2>,
widget: Option<CustomWidget>,
locked: bool,
id: Option<PlugId>,
default_pos: Option<Pos2>,
cable_active: bool,
vec: Option<Vec2>,
}
#[derive(Debug, Clone)]
pub(crate) struct DraggedPlug {
pub pos: Pos2,
pub size: Vec2,
}
impl Plug {
pub fn unplugged() -> Self {
Plug::default()
}
pub fn to<T: Hash + Eq + Debug + Send + Sync + 'static>(port: T) -> Self {
Plug {
plug_to: Some(PortId::new(port)),
..Default::default()
}
}
pub fn pos(mut self, pos: Pos2) -> Self {
self.pos = Some(pos);
self
}
pub fn widget(mut self, widget: impl Into<CustomWidget>) -> Self {
self.widget = Some(widget.into());
self
}
pub fn default_pos(mut self, pos: Pos2) -> Self {
self.default_pos = Some(pos);
self
}
pub fn lock(mut self) -> Self {
self.locked = true;
self
}
pub(crate) fn default_pos_no_overwrite(mut self, pos: Pos2) -> Self {
if self.default_pos.is_none() {
self.default_pos = Some(pos);
}
self
}
pub(crate) fn id(mut self, id: PlugId) -> Self {
self.id = Some(id);
self
}
pub(crate) fn cable_active(mut self, active: bool) -> Self {
self.cable_active = active;
self
}
pub(crate) fn vec(mut self, vec: Option<Vec2>) -> Self {
self.vec = vec;
self
}
}
#[derive(Clone, Debug)]
pub(crate) struct PlugState {
pos_offset: Vec2,
dragged: bool,
}
impl Widget for Plug {
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
let id = self.id.unwrap();
let default_pos = self.default_pos.unwrap();
let mut state = State::get_cloned(ui);
let mut plug_state = state.plug_state(&id).unwrap_or(PlugState {
pos_offset: vec2(0.0, 0.0),
dragged: false,
});
let get_pos = || {
if let Some(pos) = self.pos {
pos
} else {
default_pos + plug_state.pos_offset
}
};
let mut pos = if plug_state.dragged {
get_pos()
} else {
self.plug_to
.as_ref()
.and_then(|port_id| state.port_pos(port_id))
.unwrap_or_else(get_pos)
};
egui::Area::new(egui::Id::new(id.clone()))
.current_pos(pos)
.order(Order::Foreground)
.sense(Sense::hover())
.show(ui.ctx(), |ui| {
PlugParams {
vector: self.vec,
active: self.cable_active,
plugged: self.plug_to.is_some(),
locked: self.locked,
}
.set(ui);
if self.cable_active {
ui.ctx().move_to_top(ui.layer_id());
}
let response = self.widget.unwrap_or_else(|| DefaultPlug.into()).ui(ui);
let size = response.rect.size();
pos += response.drag_delta();
let center_pos = pos + size / 2.0;
plug_state.dragged = response.dragged();
if plug_state.dragged {
state.update_dragged_plug(DraggedPlug {
pos: center_pos,
size: response.rect.size(),
});
}
if response.drag_stopped() {
match (self.plug_to, state.hovered_port_id()) {
(_, Some(port_id)) => {
state
.ephemeral
.event_of_plug
.insert(response.id, Event::Connected { port_id });
}
(Some(_), None) => {
state
.ephemeral
.event_of_plug
.insert(response.id, Event::Disconnected);
}
_ => {}
}
}
if let Some(port_id) = state.hovered_port_id() {
if response.dragged() {
state
.ephemeral
.event_of_plug
.insert(response.id, Event::Hovered { port_id });
}
}
plug_state.pos_offset = pos - default_pos;
state.update_plug_state(id.clone(), plug_state);
state.store_to(ui);
response
})
.inner
}
}