Skip to content

Commit

Permalink
Adds pin placement style.
Browse files Browse the repository at this point in the history
  • Loading branch information
zakarumych committed Nov 29, 2024
1 parent b26e16f commit 70d6c59
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 27 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Default implementation matches old behavior.
This mechanism is meant to replace BackgroundPattern::Custom that was removed.

- PinPlacement style option in SnarlStyle
This option controls how pins are placed in the node
Inside - pins are placed inside the node frame - default, old behavior
Edge - pin centers are placed on the edge of the node frame
Outside - pins are placed outside the node frame with specified margin

### Removed

- BackgroundPattern::Custom is removed.
Expand Down
149 changes: 124 additions & 25 deletions src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,25 @@ pub struct SelectionStyle {
pub stroke: Stroke,
}

/// Controls how pins are placed in the node.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "egui-probe", derive(egui_probe::EguiProbe))]
pub enum PinPlacement {
/// Pins are placed inside the node frame.
#[default]
Inside,

/// Pins are placed on the edge of the node frame.
Edge,

/// Pins are placed outside the node frame.
Outside {
/// Margin between node frame and pins.
margin: f32,
},
}

/// Style for rendering Snarl.
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand Down Expand Up @@ -187,6 +206,13 @@ pub struct SnarlStyle {
)]
pub pin_shape: Option<PinShape>,

/// Placement of pins.
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Option::is_none", default)
)]
pub pin_placement: Option<PinPlacement>,

/// Width of wires.
#[cfg_attr(feature = "egui-probe", egui_probe(range = 0.0..))]
#[cfg_attr(
Expand Down Expand Up @@ -330,7 +356,7 @@ impl SnarlStyle {
fn get_pin_size(&self, scale: f32, style: &Style) -> f32 {
self.pin_size
.zoomed(scale)
.unwrap_or_else(|| style.spacing.interact_size.y * 0.5)
.unwrap_or_else(|| style.spacing.interact_size.y * 0.6)
}

fn get_pin_fill(&self, style: &Style) -> Color32 {
Expand All @@ -349,16 +375,20 @@ impl SnarlStyle {
self.pin_shape.unwrap_or(PinShape::Circle).into()
}

fn get_pin_placement(&self) -> PinPlacement {
self.pin_placement.unwrap_or(PinPlacement::default())
}

fn get_wire_width(&self, scale: f32, style: &Style) -> f32 {
self.wire_width
.zoomed(scale)
.unwrap_or(self.get_pin_size(scale, style) * 0.2)
.unwrap_or(self.get_pin_size(scale, style) * 0.1)
}

fn get_wire_frame_size(&self, scale: f32, style: &Style) -> f32 {
self.wire_frame_size
.zoomed(scale)
.unwrap_or(self.get_pin_size(scale, style) * 5.0)
.unwrap_or(self.get_pin_size(scale, style) * 3.0)
}

fn get_downscale_wire_frame(&self) -> bool {
Expand Down Expand Up @@ -509,6 +539,7 @@ impl SnarlStyle {
pin_fill: None,
pin_stroke: None,
pin_shape: None,
pin_placement: None,
wire_width: None,
wire_frame_size: None,
downscale_wire_frame: None,
Expand Down Expand Up @@ -733,8 +764,12 @@ impl<T> Snarl<T> {
let mut wire_hit = false;

for wire in self.wires.iter() {
let from_r = &output_info[&wire.out_pin];
let to_r = &input_info[&wire.in_pin];
let Some(from_r) = output_info.get(&wire.out_pin) else {
continue;
};
let Some(to_r) = input_info.get(&wire.in_pin) else {
continue;
};

if !wire_hit && !snarl_state.has_new_wires() && bg_r.hovered() && !bg_r.dragged() {
// Try to find hovered wire
Expand Down Expand Up @@ -1071,6 +1106,7 @@ impl<T> Snarl<T> {
viewport: Rect,
input_x: f32,
min_pin_y: f32,
input_spacing: Option<f32>,
snarl_state: &mut SnarlState,
input: &Input,
input_positions: &mut HashMap<InPinId, PinResponse>,
Expand All @@ -1094,8 +1130,9 @@ impl<T> Snarl<T> {
for in_pin in inputs {
// Show input pin.
inputs_ui.with_layout(Layout::left_to_right(Align::Min), |ui| {
// Allocate space for pin shape.
let (pin_id, _) = ui.allocate_space(vec2(pin_size * 1.5, pin_size * 1.5));
if let Some(input_spacing) = input_spacing {
ui.allocate_space(vec2(input_spacing, pin_size));
}

let y0 = ui.cursor().min.y;

Expand All @@ -1116,12 +1153,16 @@ impl<T> Snarl<T> {
let pin_pos = pos2(input_x, y);

// Interact with pin shape.
ui.set_clip_rect(viewport);

let r = ui.interact(
Rect::from_center_size(pin_pos, vec2(pin_size, pin_size)),
pin_id,
ui.next_auto_id(),
Sense::click_and_drag(),
);

ui.skip_ahead_auto_ids(1);

if r.clicked_by(PointerButton::Secondary) {
if snarl_state.has_new_wires() {
snarl_state.remove_new_wire_in(in_pin.id);
Expand Down Expand Up @@ -1151,7 +1192,7 @@ impl<T> Snarl<T> {
drag_released = true;
}

let mut pin_size = pin_size;
let mut visual_pin_size = pin_size;

match input.hover_pos {
Some(hover_pos) if r.rect.contains(hover_pos) => {
Expand All @@ -1161,27 +1202,30 @@ impl<T> Snarl<T> {
snarl_state.remove_new_wire_in(in_pin.id);
}
pin_hovered = Some(AnyPin::In(in_pin.id));
pin_size *= 1.2;
visual_pin_size *= 1.2;
}
_ => {}
}

let mut pin_painter = ui.painter().clone();
pin_painter.set_clip_rect(viewport);

let pin_color = viewer.draw_input_pin(
in_pin,
&pin_info,
pin_pos,
pin_size,
r.rect.center(),
visual_pin_size,
style,
ui.style(),
ui.painter(),
&pin_painter,
snarl_state.scale(),
self,
);

input_positions.insert(
in_pin.id,
PinResponse {
pos: pin_pos,
pos: r.rect.center(),
pin_color,
wire_style: pin_info.wire_style,
},
Expand Down Expand Up @@ -1212,6 +1256,7 @@ impl<T> Snarl<T> {
viewport: Rect,
output_x: f32,
min_pin_y: f32,
output_spacing: Option<f32>,
snarl_state: &mut SnarlState,
input: &Input,
output_positions: &mut HashMap<OutPinId, PinResponse>,
Expand All @@ -1236,8 +1281,9 @@ impl<T> Snarl<T> {
// Show output pin.
outputs_ui.with_layout(Layout::right_to_left(Align::Min), |ui| {
// Allocate space for pin shape.

let (pin_id, _) = ui.allocate_space(vec2(pin_size * 1.5, pin_size * 1.5));
if let Some(output_spacing) = output_spacing {
ui.allocate_space(vec2(output_spacing, pin_size));
}

let y0 = ui.cursor().min.y;

Expand All @@ -1257,12 +1303,16 @@ impl<T> Snarl<T> {

let pin_pos = pos2(output_x, y);

ui.set_clip_rect(viewport);

let r = ui.interact(
Rect::from_center_size(pin_pos, vec2(pin_size, pin_size)),
pin_id,
ui.next_auto_id(),
Sense::click_and_drag(),
);

ui.skip_ahead_auto_ids(1);

if r.clicked_by(PointerButton::Secondary) {
if snarl_state.has_new_wires() {
snarl_state.remove_new_wire_out(out_pin.id);
Expand Down Expand Up @@ -1293,7 +1343,7 @@ impl<T> Snarl<T> {
drag_released = true;
}

let mut pin_size = pin_size;
let mut visual_pin_size = pin_size;
match input.hover_pos {
Some(hover_pos) if r.rect.contains(hover_pos) => {
if input.modifiers.shift {
Expand All @@ -1302,27 +1352,30 @@ impl<T> Snarl<T> {
snarl_state.remove_new_wire_out(out_pin.id);
}
pin_hovered = Some(AnyPin::Out(out_pin.id));
pin_size *= 1.2;
visual_pin_size *= 1.2;
}
_ => {}
}

let mut pin_painter = ui.painter().clone();
pin_painter.set_clip_rect(viewport);

let pin_color = viewer.draw_output_pin(
out_pin,
&pin_info,
pin_pos,
pin_size,
r.rect.center(),
visual_pin_size,
style,
ui.style(),
ui.painter(),
&pin_painter,
snarl_state.scale(),
self,
);

output_positions.insert(
out_pin.id,
PinResponse {
pos: pin_pos,
pos: r.rect.center(),
pin_color,
wire_style: pin_info.wire_style,
},
Expand Down Expand Up @@ -1444,8 +1497,12 @@ impl<T> Snarl<T> {
);
}

// Size of the pin.
// Side of the square or diameter of the circle.
let pin_size = style.get_pin_size(snarl_state.scale(), ui.style()).max(0.0);

let pin_placement = style.get_pin_placement();

let header_drag_space = style
.get_header_drag_space(snarl_state.scale(), ui.style())
.max(Vec2::ZERO);
Expand Down Expand Up @@ -1520,9 +1577,45 @@ impl<T> Snarl<T> {
let r = node_frame.show(node_ui, |ui| {
let min_pin_y = node_rect.min.y + node_state.header_height() * 0.5;

let input_x = node_frame_rect.left() + node_frame.inner_margin.left + pin_size;
// Input pins' center side by X axis.
let input_x = match pin_placement {
PinPlacement::Inside => {
node_frame_rect.left() + node_frame.inner_margin.left + pin_size * 0.5
}
PinPlacement::Edge => node_frame_rect.left(),
PinPlacement::Outside { margin } => {
node_frame_rect.left() - margin - pin_size * 0.5
}
};

// Input pins' spacing required.
let input_spacing = match pin_placement {
PinPlacement::Inside => Some(pin_size),
PinPlacement::Edge => {
Some((pin_size * 0.5 - node_frame.inner_margin.left).max(0.0))
}
PinPlacement::Outside { .. } => None,
};

// Output pins' center side by X axis.
let output_x = match pin_placement {
PinPlacement::Inside => {
node_frame_rect.right() - node_frame.inner_margin.right - pin_size * 0.5
}
PinPlacement::Edge => node_frame_rect.right(),
PinPlacement::Outside { margin } => {
node_frame_rect.right() + margin + pin_size * 0.5
}
};

let output_x = node_frame_rect.right() - node_frame.inner_margin.right - pin_size;
// Output pins' spacing required.
let output_spacing = match pin_placement {
PinPlacement::Inside => Some(pin_size),
PinPlacement::Edge => {
Some((pin_size * 0.5 - node_frame.inner_margin.right).max(0.0))
}
PinPlacement::Outside { .. } => None,
};

// Input/output pin block

Expand Down Expand Up @@ -1565,6 +1658,7 @@ impl<T> Snarl<T> {
viewport,
input_x,
min_pin_y,
input_spacing,
snarl_state,
input,
input_positions,
Expand Down Expand Up @@ -1598,6 +1692,7 @@ impl<T> Snarl<T> {
viewport,
output_x,
min_pin_y,
output_spacing,
snarl_state,
input,
output_positions,
Expand Down Expand Up @@ -1677,6 +1772,7 @@ impl<T> Snarl<T> {
viewport,
input_x,
min_pin_y,
input_spacing,
snarl_state,
input,
input_positions,
Expand Down Expand Up @@ -1746,6 +1842,7 @@ impl<T> Snarl<T> {
viewport,
output_x,
min_pin_y,
output_spacing,
snarl_state,
input,
output_positions,
Expand Down Expand Up @@ -1790,6 +1887,7 @@ impl<T> Snarl<T> {
viewport,
output_x,
min_pin_y,
output_spacing,
snarl_state,
input,
output_positions,
Expand Down Expand Up @@ -1859,6 +1957,7 @@ impl<T> Snarl<T> {
viewport,
input_x,
min_pin_y,
input_spacing,
snarl_state,
input,
input_positions,
Expand Down
3 changes: 1 addition & 2 deletions src/ui/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ impl PinInfo {
.stroke
.zoomed(scale)
.unwrap_or(snarl_style.get_pin_stroke(scale, style));
let size = self.size.map_or(size, |s| s * size);

let size = self.size.zoomed(scale).unwrap_or(size);
draw_pin(painter, shape, fill, stroke, pos, size);

fill
Expand Down

0 comments on commit 70d6c59

Please sign in to comment.