improvements

This commit is contained in:
2026-05-24 19:38:19 +02:00
parent d6a1bafe47
commit 9855f68e7f
4 changed files with 88 additions and 19 deletions
Generated
+19
View File
@@ -2047,6 +2047,15 @@ dependencies = [
"slab",
]
[[package]]
name = "fuzzy-matcher"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
dependencies = [
"thread_local",
]
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -5450,6 +5459,7 @@ dependencies = [
"egui",
"egui_ltreeview",
"egui_plot",
"fuzzy-matcher",
"libsvld",
]
@@ -5595,6 +5605,15 @@ dependencies = [
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
]
[[package]]
name = "tiff"
version = "0.11.3"
+1
View File
@@ -16,3 +16,4 @@ eframe = "0.34"
egui = "0.34"
egui_plot = "0.35"
egui_ltreeview = "0.7"
fuzzy-matcher = "0.3"
+60 -16
View File
@@ -30,6 +30,24 @@ pub fn remove_plot(plots: &mut Vec<PlotGroup>, plot_id: u64) {
plots.retain(|p| p.id != plot_id);
}
/// Keep at most one empty plot; drop any further empties (keeps the
/// first one encountered so the user's stable reference doesn't vanish).
fn dedupe_empty(plots: &mut Vec<PlotGroup>) {
let mut seen_empty = false;
plots.retain(|p| {
if p.signals.is_empty() {
if seen_empty {
false
} else {
seen_empty = true;
true
}
} else {
true
}
});
}
/// Most common unit in the plot (first-wins on ties). Returns `None`
/// when the plot is empty.
pub fn majority_unit(signals: &[PlottedSignal]) -> Option<&str> {
@@ -50,13 +68,14 @@ pub fn contains_signal(plots: &[PlotGroup], signal_id: u64) -> bool {
.any(|p| p.signals.iter().any(|s| s.id == signal_id))
}
pub fn remove_signal(plots: &mut [PlotGroup], signal_id: u64) {
for plot in plots {
pub fn remove_signal(plots: &mut Vec<PlotGroup>, signal_id: u64) {
for plot in plots.iter_mut() {
if let Some(pos) = plot.signals.iter().position(|s| s.id == signal_id) {
plot.signals.remove(pos);
return;
break;
}
}
dedupe_empty(plots);
}
/// Place `sig` into the first plot whose majority unit matches and that
@@ -67,11 +86,18 @@ pub fn add_signal_auto(
sig: PlottedSignal,
) {
let unit = sig.unit.as_str();
let slot = plots.iter_mut().find(|p| {
p.signals.len() < MAX_SIGNALS_PER_PLOT && majority_unit(&p.signals) == Some(unit)
});
if let Some(plot) = slot {
plot.signals.push(sig);
// Prefer an existing empty plot, then a unit-matching plot under cap.
let slot = plots
.iter()
.position(|p| p.signals.is_empty())
.or_else(|| {
plots.iter().position(|p| {
p.signals.len() < MAX_SIGNALS_PER_PLOT
&& majority_unit(&p.signals) == Some(unit)
})
});
if let Some(i) = slot {
plots[i].signals.push(sig);
} else {
plots.push(PlotGroup {
id: *next_plot_id,
@@ -82,6 +108,9 @@ pub fn add_signal_auto(
}
pub fn new_plot(plots: &mut Vec<PlotGroup>, next_plot_id: &mut u64) {
if plots.iter().any(|p| p.signals.is_empty()) {
return;
}
plots.push(PlotGroup {
id: *next_plot_id,
signals: Vec::new(),
@@ -111,11 +140,10 @@ pub fn show_plot_list(
let unit = majority_unit(&plot.signals).unwrap_or("empty");
let label = format!("Plot {} [{}]", i + 1, unit);
let plot_id = plot.id;
let is_empty = plot.signals.is_empty();
let mut node =
NodeBuilder::dir(plot_node_id(plot.id)).label(label);
if is_empty {
node = node.context_menu(|ui| {
let node = NodeBuilder::dir(plot_node_id(plot.id))
.label(label)
.activatable(true)
.context_menu(|ui| {
if ui.button("Remove plot").clicked() {
events
.borrow_mut()
@@ -123,7 +151,6 @@ pub fn show_plot_list(
ui.close();
}
});
}
builder.node(node);
for sig in &plot.signals {
let sig_id = sig.id;
@@ -144,8 +171,24 @@ pub fn show_plot_list(
builder.close_dir();
});
for action in actions {
if let Action::Move(dd) = action {
events.borrow_mut().push(PlotListAction::Move(dd));
match action {
Action::Move(dd) => {
events.borrow_mut().push(PlotListAction::Move(dd));
}
Action::Activate(act) => {
for id in act.selected {
if id & SIGNAL_FLAG != 0 {
events
.borrow_mut()
.push(PlotListAction::RemoveSignal(id));
} else if let Some(plot_id) = decode_plot_node_id(id) {
events
.borrow_mut()
.push(PlotListAction::RemovePlot(plot_id));
}
}
}
_ => {}
}
}
});
@@ -164,6 +207,7 @@ pub fn apply_move(plots: &mut Vec<PlotGroup>, dd: DragAndDrop<u64>) {
apply_plot_move(plots, source & !PLOT_FLAG, dd.target, &dd.position);
}
}
dedupe_empty(plots);
}
fn apply_signal_move(
+8 -3
View File
@@ -2,6 +2,8 @@ use can_dbc::Dbc;
use eframe::egui;
use egui::ScrollArea;
use egui_ltreeview::{Action, TreeView, TreeViewBuilder};
use fuzzy_matcher::FuzzyMatcher;
use fuzzy_matcher::skim::SkimMatcherV2;
use libsvld::DecodeResult;
pub struct SigNode {
@@ -89,7 +91,8 @@ pub fn show_dbc_tree(
.hint_text("Filter messages/signals")
.desired_width(f32::INFINITY),
);
let needle = filter.trim().to_lowercase();
let needle = filter.trim();
let matcher = SkimMatcherV2::default();
ScrollArea::both()
.scroll([true, true])
.scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::VisibleWhenNeeded)
@@ -100,13 +103,15 @@ pub fn show_dbc_tree(
.show(ui, |builder: &mut TreeViewBuilder<'_, u64>| {
for msg in tree {
let msg_hit = needle.is_empty()
|| msg.label.to_lowercase().contains(&needle);
|| matcher.fuzzy_match(&msg.label, needle).is_some();
let matching: Vec<&SigNode> = if msg_hit {
msg.signals.iter().collect()
} else {
msg.signals
.iter()
.filter(|s| s.name.to_lowercase().contains(&needle))
.filter(|s| {
matcher.fuzzy_match(&s.name, needle).is_some()
})
.collect()
};
if !msg_hit && matching.is_empty() {