Skip to content

created scope launcher + signal selector - #435

Open
aaryapatnaik wants to merge 1 commit into
masterfrom
aarya/scope-widget
Open

created scope launcher + signal selector#435
aaryapatnaik wants to merge 1 commit into
masterfrom
aarya/scope-widget

Conversation

@aaryapatnaik

Copy link
Copy Markdown
Contributor

Signal selector system
Screenshot 2026-08-15 at 5 12 22 PM

Scope widget
Screenshot 2026-08-15 at 5 12 52 PM

@aaryapatnaik
aaryapatnaik requested a review from a team as a code owner August 16, 2026 00:14
@gitar-bot

gitar-bot Bot commented Aug 16, 2026

Copy link
Copy Markdown
CI failed: Formatting check failure in the CI build due to unformatted Rust code introduced by the PR changes.

Overview

1 CI job failed across 1 analyzed log due to a code formatting violation in the "Check formatting" check.

Failures

Rust Code Formatting Violation (confidence: high)

  • Type: tooling
  • Affected jobs: 95089696099
  • Related to change: yes
  • Root cause: The code changes in the PR do not adhere to the formatting rules defined by rustfmt, causing the formatting check step to exit with exit code 1.
  • Suggested fix: Run cargo fmt locally, review the formatting changes, and commit and push them to the branch.

Summary

  • Change-related failures: 1 code formatting check failure
  • Infrastructure/flaky failures: None
  • Recommended action: Run cargo fmt locally and push the updated formatting changes.
Code Review 👍 Approved with suggestions 0 resolved / 1 findings

Adds a scope launcher and signal selector UI component for visualizing daqapp signals. Consider resolving the stale heading and picker render issue when changing signals.

💡 Quality: Change Signal renders stale heading + picker in same frame

📄 daqapp/src/ui/scope.rs:189-203

After a signal is assigned, show unwraps and draws the heading "📊 {title}: {msg} - {signal}" and the Change Signal button; clicking it calls reset_to_picker() which nulls msg_id in the same frame, so the immediately-following if self.msg_id.is_none() block then draws the picker directly under the stale signal heading for one frame. It works but produces a brief inconsistent UI. Consider checking reset_to_picker was triggered before drawing the assigned-signal heading (e.g. capture the click result and branch before rendering the heading), removing the duplicated picker/no_dbc_placeholder block.

🤖 Prompt for agents
Code Review: Adds a scope launcher and signal selector UI component for visualizing daqapp signals. Consider resolving the stale heading and picker render issue when changing signals.

1. 💡 Quality: Change Signal renders stale heading + picker in same frame
   Files: daqapp/src/ui/scope.rs:189-203

   After a signal is assigned, `show` unwraps and draws the heading "📊 {title}: {msg} - {signal}" and the Change Signal button; clicking it calls `reset_to_picker()` which nulls `msg_id` in the same frame, so the immediately-following `if self.msg_id.is_none()` block then draws the picker directly under the stale signal heading for one frame. It works but produces a brief inconsistent UI. Consider checking `reset_to_picker` was triggered before drawing the assigned-signal heading (e.g. capture the click result and branch before rendering the heading), removing the duplicated picker/no_dbc_placeholder block.

Tip

Comment Gitar fix CI or enable auto-apply: gitar auto-apply:on

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Comment thread daqapp/src/ui/scope.rs
Comment on lines +189 to +203
let msg_name = self.msg_name.clone().unwrap();
let signal_name = self.signal_name.clone().unwrap();

// Horizontal container (heading + new Change Signal button)
ui.horizontal(|ui| {
ui.heading(format!("📊 {}: {} - {}", self.title, msg_name, signal_name));
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui.button("🔀 Change Signal").clicked() {
self.reset_to_picker();
}
});
});

// When change signal is clicked go back to the picker ui
if self.msg_id.is_none() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: Change Signal renders stale heading + picker in same frame

After a signal is assigned, show unwraps and draws the heading "📊 {title}: {msg} - {signal}" and the Change Signal button; clicking it calls reset_to_picker() which nulls msg_id in the same frame, so the immediately-following if self.msg_id.is_none() block then draws the picker directly under the stale signal heading for one frame. It works but produces a brief inconsistent UI. Consider checking reset_to_picker was triggered before drawing the assigned-signal heading (e.g. capture the click result and branch before rendering the heading), removing the duplicated picker/no_dbc_placeholder block.

Was this helpful? React with 👍 / 👎

@LelsersLasers LelsersLasers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥 It seems to work well. I put a few small comments about code style/using Rust types.

Addtionally you should

  • Rebase on master
  • run cargo fmt in the daqapp folder

Comment thread daqapp/src/widget_ids.rs
@@ -15,7 +15,16 @@ impl WidgetIds {
}

pub fn next(&mut self, kind: widget_constructor::WidgetConstructor) -> usize {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not exactly your problem as it was left over from a change I made, but I think next() can take WidgetConstructor as a reference instead of an owned value?

Something like this:

pub fn next(&mut self, kind: widget_constructor::WidgetConstructor) -> usize {

Comment thread daqapp/src/widget_ids.rs

pub fn next(&mut self, kind: widget_constructor::WidgetConstructor) -> usize {
let disc = std::mem::discriminant(&kind);
let disc = if matches!(kind, widget_constructor::WidgetConstructor::ScopeEmpty) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see why this solution works, but I don't think it may be the best for the longer term.

I think something like this could make sense?

Add a new "WidgetType" enum:

#[derive(Eq, PartialEq, Hash, Clone, Copy)]
pub enum WidgetKind {
    ViewerTable,
    ViewerList,
    Bootloader,
    Scope,
    LogParser,
    SendUi,
    BusLoad,
    BatteryVoltage,
    BatteryTemps,
    GgPlot,
    GpsPlot,
    Dynamics,
    Jitter,
    Hil,
}

Then WidgetConstructor can get a method like:

    fn kind(&self) -> WidgetKind {
        match self {
            Self::ViewerTable => WidgetKind::ViewerTable,
            Self::ViewerList => WidgetKind::ViewerList,
            Self::Bootloader => WidgetKind::Bootloader,
            Self::Scope { .. } | Self::ScopeEmpty => WidgetKind::Scope,
            Self::LogParser => WidgetKind::LogParser,
            Self::SendUi => WidgetKind::SendUi,
            Self::BusLoad => WidgetKind::BusLoad,
            Self::BatteryVoltage => WidgetKind::BatteryVoltage,
            Self::BatteryTemps => WidgetKind::BatteryTemps,
            Self::GgPlot => WidgetKind::GgPlot,
            Self::GpsPlot => WidgetKind::GpsPlot,
            Self::Dynamics => WidgetKind::Dynamics,
            Self::Jitter => WidgetKind::Jitter,
            Self::Hil => WidgetKind::Hil,
        }
    }

And widget ids looks a little more like?

pub fn next(&mut self, constructor: &WidgetConstructor) -> usize {
    let counter = self.counters.entry(constructor.kind()).or_insert(1);
    let id = *counter;
    *counter += 1;
    id
}

I think as a side effect of this, you can also clean up the Hash versus PartialEq implementations?

Comment thread daqapp/src/ui/scope.rs
return egui_tiles::UiResponse::None;
}

let msg_name = self.msg_name.clone().unwrap();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid unwrap. I think it's safe because self.msg_name.is_none() has it's code own path, but the Rust type system can help you here to avoid it

Comment thread daqapp/src/ui/scope.rs
msg_id: u32,
msg_name: String,
signal_name: String,
msg_id: Option<u32>,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my understanding, msg_id and msg_name are either both None or both Some at the same time. To get better production, you can make a mini struct and then the Scope just has Option

Comment thread daqapp/src/ui/scope.rs
msg_id: Option<u32>,
msg_name: Option<String>,
signal_name: Option<String>,
msg_picker: DbcMsgPickerState, // Picker state, only relevant while msg_id/signal_name are None (or user hit "Change Signal")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use a sum type (Rust enum) for this. You could have an enum like (psudeo code):

enum {
NoMessagePicked { DbcMsgPickerState} 
MessagePicked { msg_name, msg_id },
MessageAndSignalPicked  { msg_name, msg_id, signal_name }

Comment thread daqapp/src/ui/scope.rs
impl Scope {
// Og constructor, unchanged
pub fn new(instance_num: usize, msg_id: u32, msg_name: String, signal_name: String) -> Self {
let title = format!("Scope #{}", instance_num);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a problem that you were supposed to solve, but is it possible to make the title include the signal name instead of the instance_num?

Comment thread daqapp/src/ui/scope.rs
use egui_plot::{Line, Plot, PlotPoints};
use std::collections::VecDeque;

use super::dbc_msg_picker::{DbcMsgPickerState, no_dbc_placeholder};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small: I prefer the full qualified names (ex: add ui to the use crate:: options and then use ui::dbc_msg_picker::DbcMsgPickerState everywhere), but if you like the shorter usages elsewhere, instead of super you can do: still ui to the use crate:: but then do something like use ui::dbc_msg_picker::{DbcMsgPickerState, self} that way we are not relient on the folder structure for the use statement and the free floating function no_dbc_placeholder gets at least 1 level of namespacing.

Comment thread daqapp/src/ui/scope.rs
if let messages::MsgFromCan::ParsedMessage(parsed_msg) = msg {
if parsed_msg.decoded.msg_id != self.msg_id {
if parsed_msg.decoded.msg_id != target_msg_id {
return;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

highkey parsed_msg should have .decoded.name and you may be able to drop the reliance on message id

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants