improvements
This commit is contained in:
Generated
+19
@@ -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"
|
||||
|
||||
@@ -16,3 +16,4 @@ eframe = "0.34"
|
||||
egui = "0.34"
|
||||
egui_plot = "0.35"
|
||||
egui_ltreeview = "0.7"
|
||||
fuzzy-matcher = "0.3"
|
||||
|
||||
@@ -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)
|
||||
// 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(plot) = slot {
|
||||
plot.signals.push(sig);
|
||||
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,9 +171,25 @@ pub fn show_plot_list(
|
||||
builder.close_dir();
|
||||
});
|
||||
for action in actions {
|
||||
if let Action::Move(dd) = action {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
events.into_inner()
|
||||
@@ -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(
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user