Skip to content

Commit 2db42fd

Browse files
committed
Add per-link line width support
Extend LinkModel trait with line_width() method (default: 2.0px) and add line_width field to SimpleLink, LinkData, and the rendering pipeline. This allows each link to have its own stroke width for visual distinction.
1 parent b0ed843 commit 2db42fd

7 files changed

Lines changed: 57 additions & 27 deletions

File tree

examples/advanced/src/main.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ impl LinkModel for LinkData {
5959
fn end_pin_id(&self) -> i32 {
6060
self.end_pin_id
6161
}
62+
fn line_width(&self) -> f32 {
63+
self.line_width
64+
}
6265
}
6366

6467
/// Helper to remove items by ID from a model based on selection
@@ -224,12 +227,14 @@ fn main() {
224227
start_pin_id: 1002,
225228
end_pin_id: 2001,
226229
color: link_colors[0],
230+
line_width: 1.5, // Thin link
227231
},
228232
LinkData {
229233
id: 2,
230234
start_pin_id: 2002,
231235
end_pin_id: 3001,
232236
color: link_colors[1],
237+
line_width: 5.0, // Thick link to demonstrate feature
233238
},
234239
]));
235240
window.set_links(ModelRc::from(links.clone()));
@@ -468,7 +473,7 @@ fn main() {
468473
let color = link_colors[idx];
469474

470475
if let Some(_path) = cache.compute_link_path(output_pin, input_pin, w.get_zoom(), w.get_bezier_min_offset()) {
471-
let data = LinkData { id, start_pin_id: output_pin, end_pin_id: input_pin, color };
476+
let data = LinkData { id, start_pin_id: output_pin, end_pin_id: input_pin, color, line_width: 2.0 };
472477
links.push(data);
473478
}
474479
}
@@ -550,28 +555,27 @@ fn main() {
550555

551556
let filter_nodes_for_type = filter_nodes.clone();
552557
window.on_filter_type_changed(move |id, idx| {
553-
for i in 0..filter_nodes_for_type.row_count() {
554-
if let Some(mut node) = filter_nodes_for_type.row_data(i) {
555-
if node.id == id { node.filter_type_index = idx; filter_nodes_for_type.set_row_data(i, node); break; }
556-
}
558+
if let Some((i, mut node)) = GraphLogic::find_node_by_id(&filter_nodes_for_type, id, |n| n.id) {
559+
node.filter_type_index = idx;
560+
filter_nodes_for_type.set_row_data(i, node);
557561
}
558562
});
559563

560564
let filter_nodes_for_enable = filter_nodes.clone();
561565
window.on_filter_toggle_enabled(move |id| {
562-
for i in 0..filter_nodes_for_enable.row_count() {
563-
if let Some(mut node) = filter_nodes_for_enable.row_data(i) {
564-
if node.id == id { node.enabled = !node.enabled; filter_nodes_for_enable.set_row_data(i, node); break; }
565-
}
566+
if let Some((i, mut node)) = GraphLogic::find_node_by_id(&filter_nodes_for_enable, id, |n| n.id) {
567+
node.enabled = !node.enabled;
568+
filter_nodes_for_enable.set_row_data(i, node);
566569
}
567570
});
568571

569572
let filter_nodes_for_reset = filter_nodes.clone();
570573
window.on_filter_reset(move |id| {
571-
for i in 0..filter_nodes_for_reset.row_count() {
572-
if let Some(mut node) = filter_nodes_for_reset.row_data(i) {
573-
if node.id == id { node.processed_count = 0; node.filter_type_index = 0; node.enabled = true; filter_nodes_for_reset.set_row_data(i, node); break; }
574-
}
574+
if let Some((i, mut node)) = GraphLogic::find_node_by_id(&filter_nodes_for_reset, id, |n| n.id) {
575+
node.processed_count = 0;
576+
node.filter_type_index = 0;
577+
node.enabled = true;
578+
filter_nodes_for_reset.set_row_data(i, node);
575579
}
576580
});
577581

examples/custom-shapes/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,14 @@ fn main() {
4949
start_pin_id: 3, // Node 1 output
5050
end_pin_id: 4, // Node 2 input
5151
color: Color::from_argb_u8(255, 100, 180, 255),
52+
line_width: 2.0,
5253
},
5354
LinkData {
5455
id: 2,
5556
start_pin_id: 7, // Node 3 output
5657
end_pin_id: 4, // Node 2 input
5758
color: Color::from_argb_u8(255, 255, 180, 100),
59+
line_width: 3.0, // Thicker link
5860
},
5961
]))));
6062

examples/minimal/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ fn main() {
2323
start_pin_id: 3, // Node 1 output (node_id * 2 + 1)
2424
end_pin_id: 4, // Node 2 input (node_id * 2)
2525
color: Color::from_argb_u8(255, 100, 180, 255),
26+
line_width: 2.0,
2627
},
2728
]))));
2829

node-editor-building-blocks.slint

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ export struct LinkData {
117117
start-pin-id: int,
118118
end-pin-id: int,
119119
color: color,
120+
/// Line width in pixels (default: 2.0 if not specified)
121+
line-width: float,
120122
}
121123

122124
/// Minimap component for bird's-eye view of the node graph.

node-editor.slint

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ export component NodeEditor {
497497
for link in root.links: Link {
498498
path-commands: root.compute-link-path(link.start-pin-id, link.end-pin-id, root.geometry-version);
499499
link-color: link.color;
500+
line-width: link.line-width > 0 ? link.line-width * 1px : 2px;
500501
selected: root.is-link-selected(link.id, root.selection-version);
501502
hovered: root.hovered-link-id == link.id;
502503
}

src/graph.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use std::fmt;
1717
/// from: i32,
1818
/// to: i32,
1919
/// color: Color,
20+
/// width: f32,
2021
/// label: String, // custom field
2122
/// }
2223
///
@@ -25,6 +26,7 @@ use std::fmt;
2526
/// fn start_pin_id(&self) -> i32 { self.from }
2627
/// fn end_pin_id(&self) -> i32 { self.to }
2728
/// fn color(&self) -> Color { self.color }
29+
/// fn line_width(&self) -> f32 { self.width }
2830
/// }
2931
/// ```
3032
pub trait LinkModel {
@@ -38,6 +40,10 @@ pub trait LinkModel {
3840
fn color(&self) -> Color {
3941
Color::from_rgb_u8(255, 255, 255)
4042
}
43+
/// Line width in pixels for rendering the link (default: 2.0)
44+
fn line_width(&self) -> f32 {
45+
2.0
46+
}
4147
}
4248

4349
/// Simple link data structure implementing [`LinkModel`].
@@ -50,15 +56,21 @@ pub struct SimpleLink {
5056
pub start_pin_id: i32,
5157
pub end_pin_id: i32,
5258
pub color: Color,
59+
pub line_width: f32,
5360
}
5461

5562
impl SimpleLink {
56-
/// Create a new link with the specified endpoints and color.
63+
/// Create a new link with the specified endpoints, color, and line width.
5764
pub fn new(id: i32, start_pin_id: i32, end_pin_id: i32, color: Color) -> Self {
58-
Self { id, start_pin_id, end_pin_id, color }
65+
Self { id, start_pin_id, end_pin_id, color, line_width: 2.0 }
66+
}
67+
68+
/// Create a new link with custom line width.
69+
pub fn with_line_width(id: i32, start_pin_id: i32, end_pin_id: i32, color: Color, line_width: f32) -> Self {
70+
Self { id, start_pin_id, end_pin_id, color, line_width }
5971
}
6072

61-
/// Create a new link with default white color.
73+
/// Create a new link with default white color and default line width.
6274
pub fn with_default_color(id: i32, start_pin_id: i32, end_pin_id: i32) -> Self {
6375
Self::new(id, start_pin_id, end_pin_id, Color::from_rgb_u8(255, 255, 255))
6476
}
@@ -69,6 +81,7 @@ impl LinkModel for SimpleLink {
6981
fn start_pin_id(&self) -> i32 { self.start_pin_id }
7082
fn end_pin_id(&self) -> i32 { self.end_pin_id }
7183
fn color(&self) -> Color { self.color }
84+
fn line_width(&self) -> f32 { self.line_width }
7285
}
7386

7487
/// Trait for nodes that can be moved (dragged) in the editor.

src/links.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@
1616
//!
1717
//! // Set up link management
1818
//! let mut links = LinkManager::new(tracker.cache());
19+
//!
20+
//! // Add a link with default line width (2.0)
1921
//! links.add(SimpleLink::new(1, output_pin, input_pin, Color::from_rgb_u8(100, 180, 255)));
2022
//!
23+
//! // Or add a link with custom line width
24+
//! links.add(SimpleLink::with_line_width(2, output_pin, input_pin, Color::from_rgb_u8(255, 100, 100), 4.0));
25+
//!
2126
//! // Connect to Slint
2227
//! window.set_link_paths(links.paths());
2328
//!
@@ -46,12 +51,12 @@ struct ConcreteModelSyncer<P, F> {
4651
impl<P, F> ModelSyncer for ConcreteModelSyncer<P, F>
4752
where
4853
P: Clone + 'static,
49-
F: Fn(i32, SharedString, slint::Color) -> P,
54+
F: Fn(i32, SharedString, slint::Color, f32) -> P,
5055
{
5156
fn sync(&self, paths: &[LinkPathData]) {
5257
// Update existing rows or add new ones
5358
for (i, path) in paths.iter().enumerate() {
54-
let item = (self.constructor)(path.id, path.path_commands.clone().into(), path.color);
59+
let item = (self.constructor)(path.id, SharedString::from(path.path_commands.as_str()), path.color, path.line_width);
5560
if i < self.model.row_count() {
5661
self.model.set_row_data(i, item);
5762
} else {
@@ -114,6 +119,7 @@ struct LinkPathData {
114119
id: i32,
115120
path_commands: String,
116121
color: slint::Color,
122+
line_width: f32,
117123
}
118124

119125
impl<L, N> LinkManager<L, N>
@@ -145,11 +151,11 @@ where
145151
/// # Arguments
146152
///
147153
/// * `model` - The VecModel to sync to
148-
/// * `constructor` - Function to create path items from (id, path_commands, color)
154+
/// * `constructor` - Function to create path items from (id, path_commands, color, line_width)
149155
pub fn bind_model<P, F>(&mut self, model: Rc<VecModel<P>>, constructor: F)
150156
where
151157
P: Clone + 'static,
152-
F: Fn(i32, SharedString, slint::Color) -> P + 'static,
158+
F: Fn(i32, SharedString, slint::Color, f32) -> P + 'static,
153159
{
154160
self.syncer = Some(Box::new(ConcreteModelSyncer { model, constructor }));
155161
}
@@ -230,6 +236,7 @@ where
230236
id: link.id(),
231237
path_commands: path,
232238
color: link.color(),
239+
line_width: link.line_width(),
233240
});
234241
}
235242
}
@@ -270,19 +277,19 @@ pub trait LinkPathProvider {
270277
/// Create a Slint-compatible model of link paths.
271278
///
272279
/// The returned closure takes a constructor function that creates
273-
/// the Slint LinkPath type from (id, path_commands, color).
280+
/// the Slint LinkPath type from (id, path_commands, color, line_width).
274281
fn create_paths_model<P, F>(&self, constructor: F) -> ModelRc<P>
275282
where
276283
P: Clone + 'static,
277-
F: Fn(i32, slint::SharedString, slint::Color) -> P + 'static;
284+
F: Fn(i32, slint::SharedString, slint::Color, f32) -> P + 'static;
278285

279286
/// Update an existing paths model in place.
280287
///
281288
/// This is more efficient than creating a new model each time.
282289
fn update_paths_model<P, F>(&self, model: &VecModel<P>, constructor: F)
283290
where
284291
P: Clone + 'static,
285-
F: Fn(i32, slint::SharedString, slint::Color) -> P;
292+
F: Fn(i32, slint::SharedString, slint::Color, f32) -> P;
286293
}
287294

288295
impl<L, N> LinkPathProvider for LinkManager<L, N>
@@ -293,26 +300,26 @@ where
293300
fn create_paths_model<P, F>(&self, constructor: F) -> ModelRc<P>
294301
where
295302
P: Clone + 'static,
296-
F: Fn(i32, slint::SharedString, slint::Color) -> P + 'static,
303+
F: Fn(i32, slint::SharedString, slint::Color, f32) -> P + 'static,
297304
{
298305
let paths = self.paths.borrow();
299306
let items: Vec<P> = paths
300307
.iter()
301-
.map(|p| constructor(p.id, p.path_commands.clone().into(), p.color))
308+
.map(|p| constructor(p.id, SharedString::from(p.path_commands.as_str()), p.color, p.line_width))
302309
.collect();
303310
ModelRc::from(Rc::new(VecModel::from(items)))
304311
}
305312

306313
fn update_paths_model<P, F>(&self, model: &VecModel<P>, constructor: F)
307314
where
308315
P: Clone + 'static,
309-
F: Fn(i32, slint::SharedString, slint::Color) -> P,
316+
F: Fn(i32, slint::SharedString, slint::Color, f32) -> P,
310317
{
311318
let paths = self.paths.borrow();
312319

313320
// Update existing rows or add new ones
314321
for (i, path) in paths.iter().enumerate() {
315-
let item = constructor(path.id, path.path_commands.clone().into(), path.color);
322+
let item = constructor(path.id, SharedString::from(path.path_commands.as_str()), path.color, path.line_width);
316323
if i < model.row_count() {
317324
model.set_row_data(i, item);
318325
} else {

0 commit comments

Comments
 (0)