use crate::ids::PinId;
use iced::{Element, Event, Length, Point, Rectangle, Size};
use iced_wgpu::core::{
Clipboard, Layout, Shell, Widget, layout, mouse, renderer,
widget::{Tree, tree},
};
const DEFAULT_PIN_SIZE: Size = Size::new(50.0, 20.0);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u32)]
pub enum PinSide {
#[default]
Left = 0,
Right = 1,
Top = 2,
Bottom = 3,
Row = 4,
}
impl From<PinSide> for u32 {
fn from(side: PinSide) -> u32 {
side as u32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum PinDirection {
Input,
Output,
#[default]
Both,
}
pub struct PinInfo<'a, P, UI = ()> {
direction: PinDirection,
pin_id: &'a P,
info: &'a UI,
}
impl<'a, P, UI> PinInfo<'a, P, UI> {
pub(crate) fn new(direction: PinDirection, pin_id: &'a P, info: &'a UI) -> Self {
Self {
direction,
pin_id,
info,
}
}
pub fn direction(&self) -> PinDirection {
self.direction
}
pub fn pin_id(&self) -> &P {
self.pin_id
}
pub fn info(&self) -> &UI {
self.info
}
}
pub struct PinEnd<'a, N, P, UI = ()> {
node_id: &'a N,
pin_id: &'a P,
direction: PinDirection,
info: &'a UI,
is_occupied: bool,
}
impl<N, P, UI> Clone for PinEnd<'_, N, P, UI> {
fn clone(&self) -> Self {
*self
}
}
impl<N, P, UI> Copy for PinEnd<'_, N, P, UI> {}
impl<'a, N, P, UI> PinEnd<'a, N, P, UI> {
pub(crate) fn new(
node_id: &'a N,
pin_id: &'a P,
direction: PinDirection,
info: &'a UI,
is_occupied: bool,
) -> Self {
Self {
node_id,
pin_id,
direction,
info,
is_occupied,
}
}
pub fn node_id(&self) -> &N {
self.node_id
}
pub fn pin_id(&self) -> &P {
self.pin_id
}
pub fn direction(&self) -> PinDirection {
self.direction
}
pub fn info(&self) -> &UI {
self.info
}
pub fn is_occupied(&self) -> bool {
self.is_occupied
}
}
pub struct NodePin<'a, P, UI, Message, Theme, Renderer>
where
P: PinId,
Renderer: renderer::Renderer,
{
pub side: PinSide,
pub direction: PinDirection,
pub pin_id: P,
pub user_info: UI,
pub content: Element<'a, Message, Theme, Renderer>,
interactions_disabled: bool,
}
impl<'a, P, Message, Theme, Renderer> NodePin<'a, P, (), Message, Theme, Renderer>
where
P: PinId,
Renderer: renderer::Renderer,
{
pub fn new(
side: PinSide,
pin_id: P,
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Self {
Self {
side,
direction: PinDirection::Both,
pin_id,
user_info: (),
content: content.into(),
interactions_disabled: false,
}
}
}
impl<'a, P, UI, Message, Theme, Renderer> NodePin<'a, P, UI, Message, Theme, Renderer>
where
P: PinId,
Renderer: renderer::Renderer,
{
pub fn direction(mut self, direction: PinDirection) -> Self {
self.direction = direction;
self
}
pub fn info<UI2>(self, info: UI2) -> NodePin<'a, P, UI2, Message, Theme, Renderer> {
NodePin {
side: self.side,
direction: self.direction,
pin_id: self.pin_id,
user_info: info,
content: self.content,
interactions_disabled: self.interactions_disabled,
}
}
pub fn disable_interactions(mut self) -> Self {
self.interactions_disabled = true;
self
}
}
#[derive(Debug, Clone)]
pub(super) struct NodePinState<P, UI> {
pub pin_id: P,
pub side: PinSide,
pub direction: PinDirection,
pub position: Point,
pub interactions_disabled: bool,
pub user_info: UI,
}
impl<'a, P, UI, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for NodePin<'a, P, UI, Message, Theme, Renderer>
where
P: PinId + 'static,
UI: Clone + 'static,
Renderer: renderer::Renderer + 'a,
Theme: 'a,
Message: 'a,
{
fn tag(&self) -> tree::Tag {
tree::Tag::of::<NodePinState<P, UI>>()
}
fn state(&self) -> tree::State {
tree::State::new(NodePinState {
pin_id: self.pin_id.clone(),
side: self.side,
direction: self.direction,
position: Point::new(0.0, 0.0),
interactions_disabled: self.interactions_disabled,
user_info: self.user_info.clone(),
})
}
fn size(&self) -> Size<Length> {
self.content.as_widget().size()
}
fn children(&self) -> Vec<Tree> {
vec![Tree::new(&self.content)]
}
fn layout(
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
if let Some(content_tree) = tree.children.first_mut() {
let content_layout =
self.content
.as_widget_mut()
.layout(content_tree, renderer, limits);
let size = content_layout.size();
layout::Node::with_children(size, vec![content_layout])
} else {
layout::Node::new(DEFAULT_PIN_SIZE)
}
}
fn update(
&mut self,
tree: &mut Tree,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) {
{
let state = tree.state.downcast_mut::<NodePinState<P, UI>>();
state.pin_id = self.pin_id.clone();
state.side = self.side;
state.direction = self.direction;
state.position = layout.bounds().center();
state.interactions_disabled = self.interactions_disabled;
state.user_info = self.user_info.clone();
}
if let Some((child_layout, child_tree)) = layout.children().zip(&mut tree.children).next() {
self.content.as_widget_mut().update(
child_tree,
event,
child_layout,
cursor,
renderer,
clipboard,
shell,
viewport,
);
}
}
fn draw(
&self,
tree: &Tree,
renderer: &mut Renderer,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
) {
if let Some((child_layout, child_tree)) = layout.children().zip(&tree.children).next() {
self.content.as_widget().draw(
child_tree,
renderer,
theme,
style,
child_layout,
cursor,
viewport,
);
}
}
fn mouse_interaction(
&self,
tree: &Tree,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
if let Some((content_tree, content_layout)) =
tree.children.first().zip(layout.children().next())
{
self.content.as_widget().mouse_interaction(
content_tree,
content_layout,
cursor,
viewport,
renderer,
)
} else {
mouse::Interaction::default()
}
}
fn size_hint(&self) -> Size<Length> {
self.content.as_widget().size_hint()
}
fn diff(&self, tree: &mut Tree) {
if let Some(content_tree) = tree.children.first_mut() {
self.content.as_widget().diff(content_tree);
} else {
tree.children.push(Tree::new(&self.content));
}
}
}
impl<'a, P, UI, Message, Theme, Renderer> From<NodePin<'a, P, UI, Message, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer>
where
P: PinId + 'static,
UI: Clone + 'static,
Renderer: renderer::Renderer + 'a,
Message: 'a,
Theme: 'a,
{
fn from(widget: NodePin<'a, P, UI, Message, Theme, Renderer>) -> Self {
Element::new(widget)
}
}
pub fn node_pin<'a, P, Message, Theme, Renderer>(
side: PinSide,
pin_id: P,
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> NodePin<'a, P, (), Message, Theme, Renderer>
where
P: PinId,
Renderer: iced_wgpu::core::renderer::Renderer,
{
NodePin::new(side, pin_id, content)
}
#[macro_export]
macro_rules! pin {
($side:ident, $pin_id:expr, $content:expr, $dir:ident, $info:expr) => {
$crate::node_pin($crate::PinSide::$side, $pin_id, $content)
.direction($crate::PinDirection::$dir)
.info($info)
};
($side:ident, $pin_id:expr, $content:expr, $dir:ident) => {
$crate::node_pin($crate::PinSide::$side, $pin_id, $content)
.direction($crate::PinDirection::$dir)
};
($side:ident, $pin_id:expr, $content:expr) => {
$crate::node_pin($crate::PinSide::$side, $pin_id, $content)
};
}