Skip to content

Commit 0011fcd

Browse files
committed
Display message when trying to drag/drop a scalar into a waveform view to make it more obvious why this won't work
1 parent 21ee5be commit 0011fcd

File tree

10 files changed

+117
-40
lines changed

10 files changed

+117
-40
lines changed

release-notes/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This is a running list of significant bug fixes and new features since the last
77
* Core: Changed rate limiting sleep in InstrumentThread loop from 10ms to 1ms to avoid bogging down high performance instruments like the ThunderScope
88
* Drivers: ThunderScope now overlaps socket IO and GPU processing of waveforms giving a significant increase in WFM/s rate
99
* Filters: Added GPU acceleration for several filters including CDR PLL (7.5x speedup), 100baseTX (10x speedup), eye pattern (25x speedup), histogram (12x speedup), TIE (5.3x speedup) and more (https://github.com/ngscopeclient/scopehal/issues/977).
10-
* Filters: CDR PLL now outputs the input signal sampled by the recovered clock in a second data stream.
10+
* Filters: CDR PLL now outputs the input signal sampled by the recovered clock in a second data stream (https://github.com/ngscopeclient/scopehal/issues/991)
1111
* Filters: Peak detector for FFT etc now does quadratic interpolation for sub-sample peak fitting
1212
* Filters: Horizontal bathtub curve now works properly with MLT-3 / PAM-3 eyes as well as NRZ. No PAM-4 or higher support yet.
1313
* Filters: PcapNG export now has an additional mode selector for use with named pipes, allowing live streaming of PcapNG formatted data to WireShark
@@ -28,3 +28,5 @@ We try to maintain compatibility with older versions of ngscopeclient but occasi
2828
* GUI: Pressing middle mouse on the Y axis to autoscale would fail, setting the full scale range to zero volts, if the waveform was resident in GPU memory and the CPU-side copy of the buffer was stale
2929

3030
## Other changes since v0.1.1
31+
32+
* General UI overhaul of stream browser to make things more intuitive and reduce the number of clicks needed to perform common tasks

src/ngscopeclient/MainWindow.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* *
33
* ngscopeclient *
44
* *
5-
* Copyright (c) 2012-2025 Andrew D. Zonenberg and contributors *
5+
* Copyright (c) 2012-2026 Andrew D. Zonenberg and contributors *
66
* All rights reserved. *
77
* *
88
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
@@ -171,9 +171,7 @@ MainWindow::MainWindow(shared_ptr<QueueHandle> queue)
171171
m_toolbarIconSize = 0;
172172
LoadToolbarIcons();
173173
LoadGradients();
174-
m_texmgr.LoadTexture("warning", FindDataFile("icons/48x48/dialog-warning-2.png"));
175-
m_texmgr.LoadTexture("visible-spectrum-380nm-750nm",
176-
FindDataFile("icons/gradients/visible-spectrum-380nm-750nm.png"));
174+
LoadMiscIcons();
177175
LoadFilterIcons();
178176
LoadStatusBarIcons();
179177
LoadWaveformShapeIcons();

src/ngscopeclient/MainWindow.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* *
33
* ngscopeclient *
44
* *
5-
* Copyright (c) 2012-2025 Andrew D. Zonenberg and contributors *
5+
* Copyright (c) 2012-2026 Andrew D. Zonenberg and contributors *
66
* All rights reserved. *
77
* *
88
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
@@ -130,7 +130,7 @@ class MainWindow : public VulkanWindow
130130
{ m_splitRequests.push_back(SplitGroupRequest(group, direction, stream)); }
131131

132132
void ShowChannelProperties(OscilloscopeChannel* channel);
133-
void ShowInstrumentProperties(std::shared_ptr<Instrument> instrumet);
133+
void ShowInstrumentProperties(std::shared_ptr<Instrument> instrument);
134134
void ShowTriggerProperties();
135135
void ShowManageInstruments();
136136
void ShowSyncWizard(std::shared_ptr<TriggerGroup> group, std::shared_ptr<Oscilloscope> secondary);
@@ -275,6 +275,7 @@ class MainWindow : public VulkanWindow
275275
std::vector<std::string> m_eyeGradients;
276276

277277
void LoadFilterIcons();
278+
void LoadMiscIcons();
278279
void LoadStatusBarIcons();
279280
void LoadWaveformShapeIcons();
280281
void LoadAppIcon();

src/ngscopeclient/MainWindow_Icons.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* *
33
* ngscopeclient *
44
* *
5-
* Copyright (c) 2012-2025 Andrew D. Zonenberg and contributors *
5+
* Copyright (c) 2012-2026 Andrew D. Zonenberg and contributors *
66
* All rights reserved. *
77
* *
88
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
@@ -793,6 +793,17 @@ string MainWindow::GetIconForWaveformShape(FunctionGenerator::WaveShape shape)
793793
return "shape-default";
794794
}
795795

796+
/**
797+
@brief Load various miscellaneous icons and textures
798+
*/
799+
void MainWindow::LoadMiscIcons()
800+
{
801+
m_texmgr.LoadTexture("warning", FindDataFile("icons/48x48/dialog-warning-2.png"));
802+
m_texmgr.LoadTexture("info", FindDataFile("icons/48x48/dialog-information-3.png"));
803+
m_texmgr.LoadTexture("visible-spectrum-380nm-750nm",
804+
FindDataFile("icons/gradients/visible-spectrum-380nm-750nm.png"));
805+
}
806+
796807
/**
797808
@brief Load toolbar icons from disk if preferences changed
798809
*/

src/ngscopeclient/StreamBrowserDialog.cpp

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* *
33
* ngscopeclient *
44
* *
5-
* Copyright (c) 2012-2025 Andrew D. Zonenberg and contributors *
5+
* Copyright (c) 2012-2026 Andrew D. Zonenberg and contributors *
66
* All rights reserved. *
77
* *
88
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
@@ -1259,11 +1259,9 @@ void StreamBrowserDialog::renderChannelNode(shared_ptr<Instrument> instrument, s
12591259
auto psuchan = dynamic_cast<PowerSupplyChannel *>(channel);
12601260
auto awgchan = dynamic_cast<FunctionGeneratorChannel *>(channel);
12611261
bool renderProps = false;
1262-
bool isDigital = false;
12631262
if (scopechan)
12641263
{
12651264
renderProps = scopechan->IsEnabled();
1266-
isDigital = scopechan->GetType(0) == Stream::STREAM_TYPE_DIGITAL;
12671265
}
12681266
else if(awg && awgchan)
12691267
{
@@ -1278,20 +1276,33 @@ void StreamBrowserDialog::renderChannelNode(shared_ptr<Instrument> instrument, s
12781276
int flags = 0;
12791277
if(!hasChildren)
12801278
flags |= ImGuiTreeNodeFlags_Leaf;
1281-
1282-
//Collapse all scope channel nodes by default to reduce clutter
1283-
if(isDigital || scopechan)
1284-
{}
1285-
12861279
else
1287-
flags |= ImGuiTreeNodeFlags_DefaultOpen;
1280+
flags |= ImGuiTreeNodeFlags_OpenOnArrow;
12881281

12891282
bool open = ImGui::TreeNodeEx(
12901283
channel->GetDisplayName().c_str(),
12911284
flags);
12921285
if (channel->m_displaycolor != "")
12931286
ImGui::PopStyleColor();
12941287

1288+
//Open properties dialog on double click
1289+
if(ImGui::IsItemHovered())
1290+
{
1291+
if(ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
1292+
{
1293+
if(scopechan)
1294+
m_parent->ShowChannelProperties(scopechan);
1295+
else if(psuchan)
1296+
m_parent->ShowInstrumentProperties(psu);
1297+
else if(awgchan)
1298+
m_parent->ShowInstrumentProperties(awg);
1299+
else
1300+
LogWarning("Don't know how to open channel properties yet\n");
1301+
}
1302+
1303+
m_parent->AddStatusHelp("mouse_lmb_double", "Open properties");
1304+
}
1305+
12951306
//Single stream: drag the stream not the channel
12961307
if(singleStream)
12971308
{
@@ -1418,7 +1429,7 @@ void StreamBrowserDialog::renderChannelNode(shared_ptr<Instrument> instrument, s
14181429
for(size_t j=0; j<streamCount; j++)
14191430
{
14201431
// Iterate on each stream
1421-
renderStreamNode(instrument,channel,j,!singleStream,renderProps,(j==(streamCount-1)));
1432+
renderStreamNode(instrument,channel,j,!singleStream,renderProps);
14221433
}
14231434
}
14241435
ImGui::PopID();
@@ -1438,9 +1449,8 @@ void StreamBrowserDialog::renderChannelNode(shared_ptr<Instrument> instrument, s
14381449
@param streamIndex the index of the stream to render
14391450
@param renderName true if the name of the stream should be rendred as a selectable item
14401451
@param renderProps true if a properties block should be rendered for this stream
1441-
@param isLast true if this is the last stream of the channel
14421452
*/
1443-
void StreamBrowserDialog::renderStreamNode(shared_ptr<Instrument> instrument, InstrumentChannel* channel, size_t streamIndex, bool renderName, bool renderProps, bool isLast)
1453+
void StreamBrowserDialog::renderStreamNode(shared_ptr<Instrument> instrument, InstrumentChannel* channel, size_t streamIndex, bool renderName, bool renderProps)
14441454
{
14451455
auto scope = std::dynamic_pointer_cast<Oscilloscope>(instrument);
14461456
auto scopechan = dynamic_cast<OscilloscopeChannel *>(channel);
@@ -1452,7 +1462,6 @@ void StreamBrowserDialog::renderStreamNode(shared_ptr<Instrument> instrument, In
14521462
if (renderName)
14531463
{
14541464
ImGui::Selectable(channel->GetStreamName(streamIndex).c_str());
1455-
14561465
StreamDescriptor s(channel, streamIndex);
14571466
if(ImGui::BeginDragDropSource())
14581467
{
@@ -1475,8 +1484,7 @@ void StreamBrowserDialog::renderStreamNode(shared_ptr<Instrument> instrument, In
14751484
// Channel/stream properties block
14761485
if(renderProps && scopechan)
14771486
{
1478-
// If no properties are available for this stream, only show a "Properties" link if it is the last stream of the channel/filter
1479-
bool hasProps = isLast;
1487+
bool hasProps = false;
14801488
switch (type)
14811489
{
14821490
case Stream::STREAM_TYPE_ANALOG:
@@ -1497,8 +1505,8 @@ void StreamBrowserDialog::renderStreamNode(shared_ptr<Instrument> instrument, In
14971505
ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Borders);
14981506

14991507
Unit unit = channel->GetYAxisUnits(streamIndex);
1500-
bool clicked = false;
1501-
bool hovered = false;
1508+
bool clicked;
1509+
bool hovered;
15021510
switch (type)
15031511
{
15041512
case Stream::STREAM_TYPE_ANALOG:
@@ -1519,18 +1527,10 @@ void StreamBrowserDialog::renderStreamNode(shared_ptr<Instrument> instrument, In
15191527
//fall through
15201528
default:
15211529
{
1522-
clicked = ImGui::TextLink("Properties");
1523-
hovered = ImGui::IsItemHovered();
15241530
}
15251531
break;
15261532
}
15271533
ImGui::EndChild();
1528-
if (clicked)
1529-
{
1530-
m_parent->ShowChannelProperties(scopechan);
1531-
}
1532-
if (hovered)
1533-
m_parent->AddStatusHelp("mouse_lmb", "Open properties");
15341534
}
15351535
}
15361536
ImGui::PopID();
@@ -1551,14 +1551,22 @@ void StreamBrowserDialog::renderFilterNode(Filter* filter)
15511551
ImGui::PushStyleColor(ImGuiCol_Text, ColorFromString(filter->m_displaycolor));
15521552

15531553
//Don't expand filters with a single stream by default
1554-
int flags = 0;
1554+
int flags = ImGuiTreeNodeFlags_OpenOnArrow;
15551555
if(!singleStream)
15561556
flags |= ImGuiTreeNodeFlags_DefaultOpen;
15571557

15581558
bool open = ImGui::TreeNodeEx(filter->GetDisplayName().c_str(), flags);
15591559
if (filter->m_displaycolor != "")
15601560
ImGui::PopStyleColor();
15611561

1562+
//Open properties dialog on double click
1563+
if(ImGui::IsItemHovered())
1564+
{
1565+
if(ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
1566+
m_parent->ShowChannelProperties(filter);
1567+
m_parent->AddStatusHelp("mouse_lmb_double", "Open properties");
1568+
}
1569+
15621570
//Single stream: drag the stream not the filter
15631571
if(singleStream)
15641572
{
@@ -1593,10 +1601,8 @@ void StreamBrowserDialog::renderFilterNode(Filter* filter)
15931601

15941602
size_t streamCount = filter->GetStreamCount();
15951603
for(size_t j=0; j<streamCount; j++)
1596-
{
1597-
// Iterate on each stream
1598-
renderStreamNode(nullptr,filter,j,!singleStream,true,(j==(streamCount-1)));
1599-
}
1604+
renderStreamNode(nullptr,filter,j,!singleStream,true);
1605+
16001606
ImGui::PopID();
16011607
}
16021608

src/ngscopeclient/StreamBrowserDialog.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* *
33
* ngscopeclient *
44
* *
5-
* Copyright (c) 2012-2025 Andrew D. Zonenberg and contributors *
5+
* Copyright (c) 2012-2026 Andrew D. Zonenberg and contributors *
66
* All rights reserved. *
77
* *
88
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
@@ -168,7 +168,7 @@ class StreamBrowserDialog : public Dialog
168168
void renderChannelNode(std::shared_ptr<Instrument> instrument, size_t channelIndex, bool isLast);
169169

170170
// Rendering of a stream node
171-
void renderStreamNode(std::shared_ptr<Instrument> instrument, InstrumentChannel* channel, size_t streamIndex, bool renderName, bool renderProps, bool isLast);
171+
void renderStreamNode(std::shared_ptr<Instrument> instrument, InstrumentChannel* channel, size_t streamIndex, bool renderName, bool renderProps);
172172

173173
// Rendering of an Filter node
174174
void renderFilterNode(Filter* filter);

src/ngscopeclient/WaveformArea.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3313,6 +3313,25 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu
33133313
ImVec2 edgeSize(widthOfVerticalEdge, heightOfMiddle);
33143314
EdgeDropArea("left", ImVec2(start.x, topOfMiddle), edgeSize, ImGuiDir_Left);
33153315
EdgeDropArea("right", ImVec2(rightOfMiddle, topOfMiddle), edgeSize, ImGuiDir_Right);
3316+
3317+
//Cannot drop scalars into a waveform view. Make this a bit more obvious
3318+
auto payload = ImGui::GetDragDropPayload();
3319+
if(payload)
3320+
{
3321+
if(payload->IsDataType("Scalar"))
3322+
{
3323+
ImGui::SetCursorScreenPos(start);
3324+
ImGui::InvisibleButton("scalarDrop", size);
3325+
ImGui::SetNextItemAllowOverlap();
3326+
3327+
if(ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly))
3328+
{
3329+
DrawDropScalarMessage(
3330+
ImGui::GetWindowDrawList(),
3331+
ImVec2(start.x + size.x/4, start.y + size.y/2));
3332+
}
3333+
}
3334+
}
33163335
}
33173336

33183337
/**
@@ -3330,6 +3349,7 @@ void WaveformArea::EdgeDropArea(const string& name, ImVec2 start, ImVec2 size, I
33303349
auto payload = ImGui::GetDragDropPayload();
33313350
if(!payload)
33323351
return;
3352+
33333353
bool isWaveform = payload->IsDataType("Waveform");
33343354
bool isStream = payload->IsDataType("Stream");
33353355
if(!isWaveform && !isStream)
@@ -3650,6 +3670,42 @@ void WaveformArea::CenterRightDropArea(ImVec2 start, ImVec2 size)
36503670
rounding);
36513671
}
36523672

3673+
void WaveformArea::DrawDropScalarMessage(ImDrawList* list, ImVec2 center)
3674+
{
3675+
float iconSize = ImGui::GetFontSize() * 3;
3676+
3677+
//Warning icon
3678+
list->AddImage(
3679+
m_parent->GetTextureManager()->GetTexture("info"),
3680+
ImVec2(center.x - 0.5, center.y - iconSize/2 - 0.5),
3681+
ImVec2(center.x + iconSize + 0.5, center.y + iconSize/2 + 0.5));
3682+
3683+
//Prepare to draw text
3684+
center.x += iconSize;
3685+
string str = "Cannot add a scalar value to a waveform view.\nTry dragging to the measurements window.";
3686+
3687+
//Draw background for text
3688+
float wrapWidth = 40 * ImGui::GetFontSize();
3689+
auto textsize = ImGui::CalcTextSize(str.c_str(), nullptr, false, wrapWidth);
3690+
float padding = 5;
3691+
float wrounding = 2;
3692+
list->AddRectFilled(
3693+
ImVec2(center.x, center.y - textsize.y/2 - padding),
3694+
ImVec2(center.x + textsize.x + 2*padding, center.y + textsize.y/2 + padding),
3695+
ImGui::GetColorU32(ImGuiCol_PopupBg),
3696+
wrounding);
3697+
3698+
//Draw the text
3699+
list->AddText(
3700+
ImGui::GetFont(),
3701+
ImGui::GetFontSize(),
3702+
ImVec2(center.x + padding, center.y - textsize.y/2),
3703+
ImGui::GetColorU32(ImGuiCol_Text),
3704+
str.c_str(),
3705+
nullptr,
3706+
wrapWidth);
3707+
}
3708+
36533709
/**
36543710
@brief Display a warning message about mismatched vertical scale
36553711
*/

src/ngscopeclient/WaveformArea.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,8 @@ class WaveformArea : public SerializableObject
553553
StreamDescriptor ourStream,
554554
StreamDescriptor theirStream);
555555

556+
void DrawDropScalarMessage(ImDrawList* list, ImVec2 center);
557+
556558
void DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int numAreas);
557559
void CenterLeftDropArea(ImVec2 start, ImVec2 size);
558560
void CenterRightDropArea(ImVec2 start, ImVec2 size);
3.89 KB
Loading

src/ngscopeclient/icons/LicensingNotes.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ EXCEPT for the following icons which are from the Open Icon Library and covered
33
(details in OpenIconLibraryLicensing.txt):
44

55
dialog-warning-2.png (public domain)
6+
dialog-information-3.png (CC-BY-SA 3.0 or LGPL)

0 commit comments

Comments
 (0)