From a8128b1fd683362e6910bd781f77edb55bc3185b Mon Sep 17 00:00:00 2001 From: Frederic Borry Date: Fri, 13 Feb 2026 08:37:52 +0100 Subject: [PATCH 1/9] Prevent from dragging the same stream twice in waveform area + add visaul feedback for impossible drops. --- src/ngscopeclient/WaveformArea.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ngscopeclient/WaveformArea.cpp b/src/ngscopeclient/WaveformArea.cpp index 3184bc79..d9cf32a9 100644 --- a/src/ngscopeclient/WaveformArea.cpp +++ b/src/ngscopeclient/WaveformArea.cpp @@ -3530,8 +3530,7 @@ void WaveformArea::CenterLeftDropArea(ImVec2 start, ImVec2 size) //Don't allow dropping in the same area //Reject streams not compatible with this plot - //TODO: display nice error message - if( (desc->first == this) || !IsCompatible(stream)) + if( (desc->first == this) || !IsCompatible(stream) ||IsShowing(stream)) ok = false; else if(payload->IsDelivery()) @@ -3554,7 +3553,6 @@ void WaveformArea::CenterLeftDropArea(ImVec2 start, ImVec2 size) stream = *reinterpret_cast(spay->Data); //Reject streams not compatible with this plot - //TODO: display nice error message if not if(!IsCompatible(stream)||IsShowing(stream)) ok = false; @@ -3569,7 +3567,6 @@ void WaveformArea::CenterLeftDropArea(ImVec2 start, ImVec2 size) hover = true; StreamGroupDescriptor* streamGroup = *reinterpret_cast(sgpay->Data); //Reject streams not compatible with this plot - //TODO: display nice error message if not ok = false; for(auto channel : streamGroup->m_channels) { @@ -3589,7 +3586,10 @@ void WaveformArea::CenterLeftDropArea(ImVec2 start, ImVec2 size) } if(!ok) + { // Add visual feedback if drag source is not compatible with this area + ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); return; + } //Draw overlay target const float rounding = max(3.0f, ImGui::GetStyle().FrameRounding); From 630a4ea002781670e3ec64fa652c9a35ca09ab65 Mon Sep 17 00:00:00 2001 From: Frederic Borry Date: Fri, 13 Feb 2026 22:34:53 +0100 Subject: [PATCH 2/9] First step to stream drop insertion point in WaveformArea. --- src/ngscopeclient/WaveformArea.cpp | 315 +++++++++++++++++++++++++++- src/ngscopeclient/WaveformArea.h | 8 +- src/ngscopeclient/WaveformGroup.cpp | 66 ++++++ src/ngscopeclient/WaveformGroup.h | 4 + 4 files changed, 385 insertions(+), 8 deletions(-) diff --git a/src/ngscopeclient/WaveformArea.cpp b/src/ngscopeclient/WaveformArea.cpp index d9cf32a9..ef7e8ae8 100644 --- a/src/ngscopeclient/WaveformArea.cpp +++ b/src/ngscopeclient/WaveformArea.cpp @@ -444,6 +444,67 @@ void WaveformArea::AddStream(StreamDescriptor desc, bool persistence, const stri m_displayedChannels.push_back(chan); } +/** + * Get the position of the provided Stream in this WaveformArea + * @param desc the stream to get the position for + * @return the position of the stream if found or the number of channel in this WaveformArea otherwise + */ +size_t WaveformArea::GetStreamPosition(StreamDescriptor desc) +{ + size_t position = m_displayedChannels.size(); + for (size_t i = 0; i < m_displayedChannels.size(); ++i) + { + if (m_displayedChannels[i] && m_displayedChannels[i].get()->GetStream() == desc) + { + position = i; + break; + } + } + return position; +} + +/** + @brief Adds a new stream to this plot + */ +void WaveformArea::MoveStream(StreamDescriptor desc, size_t newPosition) +{ + // Find original position + size_t oldIndex = GetStreamPosition(desc); + if (oldIndex == m_displayedChannels.size()) + // Not found + return; + + if (oldIndex == newPosition) + // Nothing to do + return; + +/* if (oldIndex < newPosition) + { + std::rotate(m_displayedChannels.begin() + oldIndex, + m_displayedChannels.begin() + oldIndex + 1, + m_displayedChannels.begin() + newPosition + 1); + } + else + { + std::rotate(m_displayedChannels.begin() + newPosition, + m_displayedChannels.begin() + oldIndex, + m_displayedChannels.begin() + oldIndex + 1); + }*/ + + // Backup value + std::shared_ptr temp = m_displayedChannels[oldIndex]; + + // Remove from list + m_displayedChannels.erase(m_displayedChannels.begin() + oldIndex); + + // If we move after, index has to be shifted + if (oldIndex < newPosition) + newPosition--; + + // Insert at new position + m_displayedChannels.insert(m_displayedChannels.begin() + newPosition, temp); +} + /** @brief Removes the stream at a specified index */ @@ -3269,9 +3330,10 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu float topOfMiddle = start.y; float bottomOfMiddle = start.y + size.y; float widthOfMiddle = rightOfMiddle - leftOfMiddle; + bool hit = false; if(iArea == 0) { - EdgeDropArea( + hit |= EdgeDropArea( "top", ImVec2(leftOfMiddle, start.y), ImVec2(widthOfMiddle, heightOfVerticalRegion), @@ -3283,7 +3345,7 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu if(iArea == (numAreas-1)) { bottomOfMiddle -= heightOfVerticalRegion; - EdgeDropArea( + hit |= EdgeDropArea( "bottom", ImVec2(leftOfMiddle, bottomOfMiddle), ImVec2(widthOfMiddle, heightOfVerticalRegion), @@ -3298,9 +3360,13 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu if( iArea == (numAreas-1) /*|| (iArea == 0)*/ ) CenterRightDropArea(ImVec2(leftOfMiddle + widthOfMiddle/2, topOfMiddle), ImVec2(widthOfMiddle/2, heightOfMiddle)); +// ImVec2 edgeSize(widthOfVerticalEdge, heightOfVerticalRegion); ImVec2 edgeSize(widthOfVerticalEdge, heightOfMiddle); - EdgeDropArea("left", ImVec2(start.x, topOfMiddle), edgeSize, ImGuiDir_Left); - EdgeDropArea("right", ImVec2(rightOfMiddle, topOfMiddle), edgeSize, ImGuiDir_Right); + hit |= EdgeDropArea("left", ImVec2(start.x, topOfMiddle), edgeSize, ImGuiDir_Left); + hit |= EdgeDropArea("right", ImVec2(rightOfMiddle, topOfMiddle), edgeSize, ImGuiDir_Right); + + if(!hit) + CenterDropArea(start, ImVec2(size.x, size.y)); //Cannot drop scalars into a waveform view. Make this a bit more obvious auto payload = ImGui::GetDragDropPayload(); @@ -3327,7 +3393,7 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu Dropping a waveform in here splits and forms a new group */ -void WaveformArea::EdgeDropArea(const string& name, ImVec2 start, ImVec2 size, ImGuiDir splitDir) +bool WaveformArea::EdgeDropArea(const string& name, ImVec2 start, ImVec2 size, ImGuiDir splitDir) { ImGui::SetCursorScreenPos(start); ImGui::InvisibleButton(name.c_str(), size); @@ -3336,13 +3402,13 @@ void WaveformArea::EdgeDropArea(const string& name, ImVec2 start, ImVec2 size, I auto payload = ImGui::GetDragDropPayload(); if(!payload) - return; + return false; bool isWaveform = payload->IsDataType("Waveform"); bool isStream = payload->IsDataType("Stream"); bool isStreamGroup = payload->IsDataType("StreamGroup"); if(!isWaveform && !isStream && !isStreamGroup) - return; + return false; //Add drop target StreamDescriptor stream; @@ -3470,6 +3536,8 @@ void WaveformArea::EdgeDropArea(const string& name, ImVec2 start, ImVec2 size, I ImVec2(center.x + lineSizeX/2 + 0.5, center.y - 0.5), lineColor); } + + return hover; } /** @@ -3627,6 +3695,239 @@ void WaveformArea::CenterLeftDropArea(ImVec2 start, ImVec2 size) } } +/** + @brief Drop area between two waveform areas in a waveform group + + Used to reorder waveform areas within the waveform group + */ +void WaveformArea::CenterDropArea(ImVec2 start, ImVec2 size) +{ + ImGui::SetCursorScreenPos(start); + ImGui::InvisibleButton("center", size); + //ImGui::Button("center", size); + ImGui::SetNextItemAllowOverlap(); + + auto payload = ImGui::GetDragDropPayload(); + if(!payload) + return; + bool isWaveform = payload->IsDataType("Waveform"); + if(!isWaveform) + return; + + //Peek the payload. If not compatible, don't even display the target + auto wpay = ImGui::GetDragDropPayload(); + if(!wpay) + return; + + auto desc = reinterpret_cast(wpay->Data); + StreamDescriptor stream = desc->first->GetStream(desc->second); + size_t position = GetStreamPosition(stream); + if(position >= m_displayedChannels.size()) + // Stream not found in this area + return; + + // Round y position to the nearrest space between channel label + ImVec2 mousePos = ImGui::GetMousePos(); + float channelLabeHeight = ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.y; + size_t insertionIndex = (mousePos.y+(channelLabeHeight/2)-start.y) / channelLabeHeight; + insertionIndex = min(insertionIndex,m_displayedChannels.size()); + float insertionYPosition = insertionIndex * channelLabeHeight; + + //Add drop target + bool ok = true; + bool hover = false; + if(ImGui::BeginDragDropTarget()) + { + //Accept drag/drop payloads from this WaveformArea + wpay = ImGui::AcceptDragDropPayload("Waveform", + ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect); + if(wpay) + { + hover = true; + + //Only drop from the same waveform area + if(!(desc->first == this)) + ok = false; + + else if(payload->IsDelivery()) + { // TODO calculate position + MoveStream(stream,insertionIndex); + } + } + ImGui::EndDragDropTarget(); + } + + if(!ok) + { // Add visual feedback if drag source is not compatible with this area + ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); + return; + } + + if(hover) + { + //Draw overlay target + const ImU32 lineColor = ImGui::GetColorU32(ImGuiCol_DockingPreview, 1.00f); + ImVec2 center(start.x + size.x/2, start.y + insertionYPosition); + float fillSize = size.x; + float lineSize = size.x-2; + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddLine( + ImVec2(center.x - lineSize/2 - 0.5, center.y), + ImVec2(center.x + lineSize/2 + 0.5, center.y), + lineColor, + 2); + } +} + + +/** + @brief Drop area between two waveform areas in a waveform group + + Used to reorder waveform areas within the waveform group + */ +void WaveformArea::BetweenWaveformsDropArea(ImVec2 start, ImVec2 size) +{ + ImGui::SetCursorScreenPos(start); + ImGui::InvisibleButton("centersplit", size); + //ImGui::Button("center", size); + ImGui::SetNextItemAllowOverlap(); + + auto payload = ImGui::GetDragDropPayload(); + if(!payload) + return; + bool isWaveform = payload->IsDataType("Waveform"); + bool isStream = payload->IsDataType("Stream"); + bool isStreamGroup = payload->IsDataType("StreamGroup"); + if(!isWaveform && !isStream && !isStreamGroup) + return; + + //Peek the payload. If not compatible, don't even display the target + /*auto desc = reinterpret_cast(wpay->Data); + size_t position = m_group->GetAreaPosition(desc->first); + if(position >= m_group->GetWaveformAreas().size()) + // WaveformArea not found in the parent WaveformGroup + return; + StreamDescriptor stream = desc->first->GetStream(desc->second); + */ + + //Add drop target + StreamDescriptor stream; + bool hover = false; + if(ImGui::BeginDragDropTarget()) + { + auto wpay = ImGui::AcceptDragDropPayload("Waveform", ImGuiDragDropFlags_AcceptPeekOnly); + if(wpay) + { + hover = true; + + auto desc = reinterpret_cast(wpay->Data); + stream = desc->first->GetStream(desc->second); + + if( (stream.GetXAxisUnits() == m_group->GetXAxisUnit()) && payload->IsDelivery() ) + { + auto area = make_shared(stream, m_group, m_parent); + m_group->AddArea(area); + + //If the stream is is a density function, propagate the color ramp selection + switch(stream.GetType()) + { + case Stream::STREAM_TYPE_EYE: + case Stream::STREAM_TYPE_SPECTROGRAM: + case Stream::STREAM_TYPE_WATERFALL: + case Stream::STREAM_TYPE_CONSTELLATION: + { + auto sdc = desc->first->GetDisplayedChannel(desc->second); + area->GetDisplayedChannel(0)->m_colorRamp = sdc->m_colorRamp; + } + break; + + default: + break; + } + + //Remove the stream from the originating waveform area + desc->first->RemoveStream(desc->second); + } + } + + //Accept drag/drop payloads from the stream browser + auto spay = ImGui::AcceptDragDropPayload("Stream", ImGuiDragDropFlags_AcceptPeekOnly); + if(spay) + { + hover = true; + stream = *reinterpret_cast(spay->Data); + + if( (stream.GetXAxisUnits() == m_group->GetXAxisUnit()) && payload->IsDelivery() ) + { + auto area = make_shared(stream, m_group, m_parent); + m_group->AddArea(area); + } + } + + //Accept drag/drop payloads for digital banks + auto sgpay = ImGui::AcceptDragDropPayload("StreamGroup", ImGuiDragDropFlags_AcceptPeekOnly); + if(sgpay) + { + hover = true; + StreamGroupDescriptor* streamGroup = *reinterpret_cast(sgpay->Data); + + if( (streamGroup->GetXAxisUnits() == m_group->GetXAxisUnit()) && payload->IsDelivery() ) + { + std::shared_ptr area; + bool first = true; + bool singleArea = ImGui::IsKeyDown(ImGuiKey_LeftShift)||ImGui::IsKeyDown(ImGuiKey_RightShift); + for(auto channel : streamGroup->m_channels) + { + StreamDescriptor s(channel, 0); + if(first || !singleArea) + { + area = make_shared(s, m_group, m_parent); + m_group->AddArea(area); + first = false; + } + else + { + area->AddStream(s); + } + } + } + } + + ImGui::EndDragDropTarget(); + } + + //Draw overlay target + const float rounding = max(3.0f, ImGui::GetStyle().FrameRounding); + const ImU32 bgBase = ImGui::GetColorU32(ImGuiCol_DockingPreview, 0.70f); + const ImU32 bgHovered = ImGui::GetColorU32(ImGuiCol_DockingPreview, 1.00f); + const ImU32 lineColor = ImGui::GetColorU32(ImGuiCol_NavWindowingHighlight, 0.60f); + ImVec2 center(start.x + size.x/2, start.y + size.y/2); + float fillSize = 34; + float lineSize = 32; + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + + //Draw background and outline + draw_list->AddRectFilled( + ImVec2(center.x - fillSize/2 - 0.5, center.y - fillSize/2 - 0.5), + ImVec2(center.x + fillSize/2 + 0.5, center.y + fillSize/2 + 0.5), + hover ? bgHovered : bgBase, + rounding); + draw_list->AddRect( + ImVec2(center.x - lineSize/2 - 0.5, center.y - lineSize/2 - 0.5), + ImVec2(center.x + lineSize/2 + 0.5, center.y - 0.5), + lineColor, + rounding); + + draw_list->AddRect( + ImVec2(center.x - lineSize/2 - 0.5, center.y + 0.5), + ImVec2(center.x + lineSize/2 + 0.5, center.y + lineSize/2 + 0.5), + lineColor, + rounding); +} + + /** @brief Drop area for the middle of the plot diff --git a/src/ngscopeclient/WaveformArea.h b/src/ngscopeclient/WaveformArea.h index 4f28ca4f..94553adc 100644 --- a/src/ngscopeclient/WaveformArea.h +++ b/src/ngscopeclient/WaveformArea.h @@ -486,6 +486,10 @@ class WaveformArea : public SerializableObject void AddStream(StreamDescriptor desc, bool persistence = false, const std::string& ramp = "eye-gradient-viridis"); + size_t GetStreamPosition(StreamDescriptor desc); + + void MoveStream(StreamDescriptor desc, size_t newPosition); + bool IsCompatible(StreamDescriptor desc); bool IsShowing(StreamDescriptor desc); @@ -562,7 +566,9 @@ class WaveformArea : public SerializableObject void DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int numAreas); void CenterLeftDropArea(ImVec2 start, ImVec2 size); void CenterRightDropArea(ImVec2 start, ImVec2 size); - void EdgeDropArea(const std::string& name, ImVec2 start, ImVec2 size, ImGuiDir splitDir); + bool EdgeDropArea(const std::string& name, ImVec2 start, ImVec2 size, ImGuiDir splitDir); + void CenterDropArea(ImVec2 start, ImVec2 size); + void BetweenWaveformsDropArea(ImVec2 start, ImVec2 size); void FilterMenu(std::shared_ptr chan); void FilterSubmenu(std::shared_ptr chan, const std::string& name, Filter::Category cat); diff --git a/src/ngscopeclient/WaveformGroup.cpp b/src/ngscopeclient/WaveformGroup.cpp index b8c4258a..78b95d6e 100644 --- a/src/ngscopeclient/WaveformGroup.cpp +++ b/src/ngscopeclient/WaveformGroup.cpp @@ -102,6 +102,72 @@ void WaveformGroup::AddArea(shared_ptr& area) m_parent->RefreshStreamBrowserDialog(); } +/** + * Get the position of the provided WavformArea in this waveform group + * @param area the area to get the position for + * @return the position of the area if found the size of this WaveformGroup otherwise + */ +size_t WaveformGroup::GetAreaPosition(WaveformArea& area) +{ + lock_guard lock(m_areaMutex); + { + size_t position = m_areas.size(); + for (size_t i = 0; i < m_areas.size(); ++i) + { + if (m_areas[i] && m_areas[i].get() == &area) + { + position = i; + break; + } + } + return position; + } +} + +void WaveformGroup::MoveArea(WaveformArea& area, size_t newPosition) +{ + lock_guard lock(m_areaMutex); + { + // Find original position + size_t oldIndex = GetAreaPosition(area); + if (oldIndex == m_areas.size()) + // Not found + return; + + if (oldIndex == newPosition) + // Nothing to do + return; + +/* if (oldIndex < newPosition) + { + std::rotate(m_areas.begin() + oldIndex, + m_areas.begin() + oldIndex + 1, + m_areas.begin() + newPosition + 1); + } + else + { + std::rotate(m_areas.begin() + newPosition, + m_areas.begin() + oldIndex, + m_areas.begin() + oldIndex + 1); + }*/ + + // Backup value + std::shared_ptr temp = m_areas[oldIndex]; + + // Remove from list + m_areas.erase(m_areas.begin() + oldIndex); + + // If we move after, index has to be shifted + if (oldIndex < newPosition) + newPosition--; + + // Insert at new position + m_areas.insert(m_areas.begin() + newPosition, temp); + } + + m_parent->RefreshStreamBrowserDialog(); +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Rendering diff --git a/src/ngscopeclient/WaveformGroup.h b/src/ngscopeclient/WaveformGroup.h index bce7dafd..7ea02caa 100644 --- a/src/ngscopeclient/WaveformGroup.h +++ b/src/ngscopeclient/WaveformGroup.h @@ -68,6 +68,10 @@ class WaveformGroup : public SerializableObject void AddArea(std::shared_ptr& area); + size_t GetAreaPosition(WaveformArea& area); + + void MoveArea(WaveformArea& area, size_t newPosition); + void OnZoomInHorizontal(int64_t target, float step); void OnZoomOutHorizontal(int64_t target, float step); void OnPanHorizontal(float step); From 795d01e37366584d9d9b6539108f1984a5a410b1 Mon Sep 17 00:00:00 2001 From: Frederic Borry Date: Sat, 14 Feb 2026 01:18:08 +0100 Subject: [PATCH 3/9] Start replacing CenterLeftDropArea. --- src/ngscopeclient/WaveformArea.cpp | 271 +++++++++++++++++++++++------ src/ngscopeclient/WaveformArea.h | 2 + 2 files changed, 222 insertions(+), 51 deletions(-) diff --git a/src/ngscopeclient/WaveformArea.cpp b/src/ngscopeclient/WaveformArea.cpp index ef7e8ae8..03f8ab4e 100644 --- a/src/ngscopeclient/WaveformArea.cpp +++ b/src/ngscopeclient/WaveformArea.cpp @@ -44,6 +44,9 @@ #include "imgui_internal.h" //for SetItemUsingMouseWheel +#define FILL_SIZE 34 +#define LINE_SIZE 32 + using namespace std; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -444,6 +447,24 @@ void WaveformArea::AddStream(StreamDescriptor desc, bool persistence, const stri m_displayedChannels.push_back(chan); } +/** + @brief Adds a new stream to this plot at given position + */ +void WaveformArea::AddStream(StreamDescriptor desc, size_t position, bool persistence, const string& ramp) +{ + auto chan = make_shared(desc, m_parent->GetSession()); + chan->SetPersistenceEnabled(persistence); + chan->m_colorRamp = ramp; + if(position >= m_displayedChannels.size()) + { + m_displayedChannels.push_back(chan); + } + else + { + m_displayedChannels.insert(m_displayedChannels.begin() + position, chan); + } +} + /** * Get the position of the provided Stream in this WaveformArea * @param desc the stream to get the position for @@ -468,41 +489,28 @@ size_t WaveformArea::GetStreamPosition(StreamDescriptor desc) */ void WaveformArea::MoveStream(StreamDescriptor desc, size_t newPosition) { - // Find original position - size_t oldIndex = GetStreamPosition(desc); - if (oldIndex == m_displayedChannels.size()) - // Not found - return; - - if (oldIndex == newPosition) - // Nothing to do - return; + // Find original position + size_t oldIndex = GetStreamPosition(desc); + if (oldIndex == m_displayedChannels.size()) + // Not found + return; -/* if (oldIndex < newPosition) - { - std::rotate(m_displayedChannels.begin() + oldIndex, - m_displayedChannels.begin() + oldIndex + 1, - m_displayedChannels.begin() + newPosition + 1); - } - else - { - std::rotate(m_displayedChannels.begin() + newPosition, - m_displayedChannels.begin() + oldIndex, - m_displayedChannels.begin() + oldIndex + 1); - }*/ + if (oldIndex == newPosition) + // Nothing to do + return; - // Backup value - std::shared_ptr temp = m_displayedChannels[oldIndex]; + // Backup value + std::shared_ptr temp = m_displayedChannels[oldIndex]; - // Remove from list - m_displayedChannels.erase(m_displayedChannels.begin() + oldIndex); + // Remove from list + m_displayedChannels.erase(m_displayedChannels.begin() + oldIndex); - // If we move after, index has to be shifted - if (oldIndex < newPosition) - newPosition--; + // If we move after, index has to be shifted + if (oldIndex < newPosition) + newPosition--; - // Insert at new position - m_displayedChannels.insert(m_displayedChannels.begin() + newPosition, temp); + // Insert at new position + m_displayedChannels.insert(m_displayedChannels.begin() + newPosition, temp); } /** @@ -3323,12 +3331,12 @@ void WaveformArea::RenderBERLevelArrows(ImVec2 start, ImVec2 /*size*/) void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int numAreas) { //Drag/drop areas for splitting - float heightOfVerticalRegion = size.y * 0.25; - float widthOfVerticalEdge = size.x*0.25; + float heightOfVerticalRegion = max(size.y*0.1,(double)FILL_SIZE); + float widthOfVerticalEdge = max(size.x*0.1,(double)FILL_SIZE); float leftOfMiddle = start.x + widthOfVerticalEdge; - float rightOfMiddle = start.x + size.x*0.75; - float topOfMiddle = start.y; - float bottomOfMiddle = start.y + size.y; + float rightOfMiddle = start.x + size.x - widthOfVerticalEdge; + float topOfMiddle = start.y + (size.y / 2) - heightOfVerticalRegion; + float bottomOfMiddle = start.y + (size.y / 2) + heightOfVerticalRegion;; float widthOfMiddle = rightOfMiddle - leftOfMiddle; bool hit = false; if(iArea == 0) @@ -3354,14 +3362,14 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu float heightOfMiddle = bottomOfMiddle - topOfMiddle; - CenterLeftDropArea(ImVec2(leftOfMiddle, topOfMiddle), ImVec2(widthOfMiddle/2, heightOfMiddle)); + //CenterLeftDropArea(ImVec2(leftOfMiddle, topOfMiddle), ImVec2(widthOfMiddle/2, heightOfMiddle)); //Only show split in bottom (or top?) area - if( iArea == (numAreas-1) /*|| (iArea == 0)*/ ) - CenterRightDropArea(ImVec2(leftOfMiddle + widthOfMiddle/2, topOfMiddle), ImVec2(widthOfMiddle/2, heightOfMiddle)); + //if( iArea == (numAreas-1) /*|| (iArea == 0)*/ ) + // CenterRightDropArea(ImVec2(leftOfMiddle + widthOfMiddle/2, topOfMiddle), ImVec2(widthOfMiddle/2, heightOfMiddle)); -// ImVec2 edgeSize(widthOfVerticalEdge, heightOfVerticalRegion); - ImVec2 edgeSize(widthOfVerticalEdge, heightOfMiddle); + ImVec2 edgeSize(widthOfVerticalEdge, heightOfVerticalRegion); +// ImVec2 edgeSize(widthOfVerticalEdge, heightOfMiddle); hit |= EdgeDropArea("left", ImVec2(start.x, topOfMiddle), edgeSize, ImGuiDir_Left); hit |= EdgeDropArea("right", ImVec2(rightOfMiddle, topOfMiddle), edgeSize, ImGuiDir_Right); @@ -3470,10 +3478,10 @@ bool WaveformArea::EdgeDropArea(const string& name, ImVec2 start, ImVec2 size, I const ImU32 bgHovered = ImGui::GetColorU32(ImGuiCol_DockingPreview, 1.00f); const ImU32 lineColor = ImGui::GetColorU32(ImGuiCol_NavWindowingHighlight, 0.60f); ImVec2 center(start.x + size.x/2, start.y + size.y/2); - float fillSizeX = 34; - float lineSizeX = 32; - float fillSizeY = 34; - float lineSizeY = 32; + float fillSizeX = FILL_SIZE; + float lineSizeX = LINE_SIZE; + float fillSizeY = FILL_SIZE; + float lineSizeY = LINE_SIZE; //L-R split: make target half size in X axis if( (splitDir == ImGuiDir_Left) || (splitDir == ImGuiDir_Right) ) @@ -3665,8 +3673,8 @@ void WaveformArea::CenterLeftDropArea(ImVec2 start, ImVec2 size) const ImU32 bgHovered = ImGui::GetColorU32(ImGuiCol_DockingPreview, 1.00f); const ImU32 lineColor = ImGui::GetColorU32(ImGuiCol_NavWindowingHighlight, 0.60f); ImVec2 center(start.x + size.x/2, start.y + size.y/2); - float fillSize = 34; - float lineSize = 32; + float fillSize = FILL_SIZE; + float lineSize = LINE_SIZE; ImDrawList* draw_list = ImGui::GetWindowDrawList(); draw_list->AddRectFilled( @@ -3707,6 +3715,167 @@ void WaveformArea::CenterDropArea(ImVec2 start, ImVec2 size) //ImGui::Button("center", size); ImGui::SetNextItemAllowOverlap(); + auto payload = ImGui::GetDragDropPayload(); + if(!payload) + return; + bool isWaveform = payload->IsDataType("Waveform"); + bool isStream = payload->IsDataType("Stream"); + bool isStreamGroup = payload->IsDataType("StreamGroup"); + if(!isWaveform && !isStream &&!isStreamGroup) + return; + + //Peek the payload. If not compatible, don't even display the target + StreamDescriptor stream; + auto peekPayload = ImGui::GetDragDropPayload(); + if(peekPayload) + { + if(isWaveform) + { + auto peekDesc = reinterpret_cast(peekPayload->Data); + stream = peekDesc->first->GetStream(peekDesc->second); + if(!IsCompatible(stream)) + return; + } + else if(isStream) + { + stream = *reinterpret_cast(peekPayload->Data); + if(!IsCompatible(stream)) + return; + } + } + + // Round y position to the nearrest space between channel label + ImVec2 mousePos = ImGui::GetMousePos(); + float channelLabeHeight = ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.y; + size_t insertionIndex = (mousePos.y+(channelLabeHeight/2)-start.y) / channelLabeHeight; + insertionIndex = min(insertionIndex,m_displayedChannels.size()); + float insertionYPosition = insertionIndex * channelLabeHeight; + + //Add drop target + bool ok = true; + bool hover = false; + if(ImGui::BeginDragDropTarget()) + { + //Accept drag/drop payloads from another WaveformArea + auto wpay = ImGui::AcceptDragDropPayload("Waveform", + ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect); + if(wpay) + { + hover = true; + + auto desc = reinterpret_cast(wpay->Data); + stream = desc->first->GetStream(desc->second); + + bool isSelf = (desc->first == this); + + //Don't allow dropping in the same area + //Reject streams not compatible with this plot + if(!IsCompatible(stream) || (IsShowing(stream)&&!isSelf)) + ok = false; + + else if(payload->IsDelivery()) + { + if(isSelf) + { // Move a stream within this area + MoveStream(stream,insertionIndex); + } + else + { + //Add the new stream to us + //TODO: copy view settings from the DisplayedChannel over? + AddStream(stream,insertionIndex); + + //Remove the stream from the originating waveform area + desc->first->RemoveStream(desc->second); + } + } + } + + //Accept drag/drop payloads from the stream browser + auto spay = ImGui::AcceptDragDropPayload("Stream", + ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect); + if(spay) + { + hover = true; + stream = *reinterpret_cast(spay->Data); + + //Reject streams not compatible with this plot + if(!IsCompatible(stream)||IsShowing(stream)) + ok = false; + + else if(payload->IsDelivery()) + AddStream(stream,insertionIndex); + } + //Accept stream group + auto sgpay = ImGui::AcceptDragDropPayload("StreamGroup", + ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect); + if(sgpay) + { + hover = true; + StreamGroupDescriptor* streamGroup = *reinterpret_cast(sgpay->Data); + //Reject streams not compatible with this plot + ok = false; + for(auto channel : streamGroup->m_channels) + { + StreamDescriptor s(channel, 0); + if(IsCompatible(s) && !IsShowing(s)) + { // At least one stream is compatible + ok = true; + if(payload->IsDelivery()) + { + AddStream(s,insertionIndex); + } + } + } + } + + ImGui::EndDragDropTarget(); + } + + if(!ok) + { // Add visual feedback if drag source is not compatible with this area + ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); + return; + } + + //Draw overlay target + if(hover) + { + //Draw overlay target + const ImU32 lineColor = ImGui::GetColorU32(ImGuiCol_DockingPreview, 1.00f); + ImVec2 center(start.x + size.x/2, start.y + insertionYPosition); + float fillSize = size.x; + float lineSize = size.x-2; + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddLine( + ImVec2(center.x - lineSize/2 - 0.5, center.y), + ImVec2(center.x + lineSize/2 + 0.5, center.y), + lineColor, + 2); + + //If trying to drop into the center marker, display warning if incompatible scales + //(new signal has significantly wider range) and not a filter + if(hover) + { + if(stream.GetType() != Stream::STREAM_TYPE_ANALOG) + return; + auto chan = stream.m_channel; + if(dynamic_cast(chan) != nullptr) + return; + + center.x += fillSize; + DrawDropRangeMismatchMessage(draw_list, center, GetFirstAnalogStream(), stream); + } + } + + +/* + ImGui::SetCursorScreenPos(start); + ImGui::InvisibleButton("center", size); + //ImGui::Button("center", size); + ImGui::SetNextItemAllowOverlap(); + auto payload = ImGui::GetDragDropPayload(); if(!payload) return; @@ -3777,7 +3946,7 @@ void WaveformArea::CenterDropArea(ImVec2 start, ImVec2 size) ImVec2(center.x + lineSize/2 + 0.5, center.y), lineColor, 2); - } + }*/ } @@ -3903,8 +4072,8 @@ void WaveformArea::BetweenWaveformsDropArea(ImVec2 start, ImVec2 size) const ImU32 bgHovered = ImGui::GetColorU32(ImGuiCol_DockingPreview, 1.00f); const ImU32 lineColor = ImGui::GetColorU32(ImGuiCol_NavWindowingHighlight, 0.60f); ImVec2 center(start.x + size.x/2, start.y + size.y/2); - float fillSize = 34; - float lineSize = 32; + float fillSize = FILL_SIZE; + float lineSize = LINE_SIZE; ImDrawList* draw_list = ImGui::GetWindowDrawList(); @@ -4041,8 +4210,8 @@ void WaveformArea::CenterRightDropArea(ImVec2 start, ImVec2 size) const ImU32 bgHovered = ImGui::GetColorU32(ImGuiCol_DockingPreview, 1.00f); const ImU32 lineColor = ImGui::GetColorU32(ImGuiCol_NavWindowingHighlight, 0.60f); ImVec2 center(start.x + size.x/2, start.y + size.y/2); - float fillSize = 34; - float lineSize = 32; + float fillSize = FILL_SIZE; + float lineSize = LINE_SIZE; ImDrawList* draw_list = ImGui::GetWindowDrawList(); diff --git a/src/ngscopeclient/WaveformArea.h b/src/ngscopeclient/WaveformArea.h index 94553adc..9e6a5e42 100644 --- a/src/ngscopeclient/WaveformArea.h +++ b/src/ngscopeclient/WaveformArea.h @@ -486,6 +486,8 @@ class WaveformArea : public SerializableObject void AddStream(StreamDescriptor desc, bool persistence = false, const std::string& ramp = "eye-gradient-viridis"); + void AddStream(StreamDescriptor desc, size_t position, bool persistence = false, const std::string& ramp = "eye-gradient-viridis"); + size_t GetStreamPosition(StreamDescriptor desc); void MoveStream(StreamDescriptor desc, size_t newPosition); From 8ebe7ac25054cc8089e158941e5e9435a90f93f2 Mon Sep 17 00:00:00 2001 From: Frederic Borry Date: Sat, 14 Feb 2026 01:20:14 +0100 Subject: [PATCH 4/9] Fixed bank ordering. --- src/ngscopeclient/WaveformArea.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ngscopeclient/WaveformArea.cpp b/src/ngscopeclient/WaveformArea.cpp index 03f8ab4e..2a5962b8 100644 --- a/src/ngscopeclient/WaveformArea.cpp +++ b/src/ngscopeclient/WaveformArea.cpp @@ -3815,9 +3815,9 @@ void WaveformArea::CenterDropArea(ImVec2 start, ImVec2 size) StreamGroupDescriptor* streamGroup = *reinterpret_cast(sgpay->Data); //Reject streams not compatible with this plot ok = false; - for(auto channel : streamGroup->m_channels) + for(auto channel = streamGroup->m_channels.rbegin(); channel != streamGroup->m_channels.rend(); ++channel) { - StreamDescriptor s(channel, 0); + StreamDescriptor s(*channel, 0); if(IsCompatible(s) && !IsShowing(s)) { // At least one stream is compatible ok = true; From 62047cbb66d5d68bb32e4f6bf13bf423a1800565 Mon Sep 17 00:00:00 2001 From: Frederic Borry Date: Sat, 14 Feb 2026 23:34:43 +0100 Subject: [PATCH 5/9] Code cleanup. Added wave group insertion point. --- src/ngscopeclient/WaveformArea.cpp | 302 +++++----------------------- src/ngscopeclient/WaveformArea.h | 3 +- src/ngscopeclient/WaveformGroup.cpp | 21 ++ src/ngscopeclient/WaveformGroup.h | 2 + 4 files changed, 72 insertions(+), 256 deletions(-) diff --git a/src/ngscopeclient/WaveformArea.cpp b/src/ngscopeclient/WaveformArea.cpp index 2a5962b8..351c3f80 100644 --- a/src/ngscopeclient/WaveformArea.cpp +++ b/src/ngscopeclient/WaveformArea.cpp @@ -3331,47 +3331,46 @@ void WaveformArea::RenderBERLevelArrows(ImVec2 start, ImVec2 /*size*/) void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int numAreas) { //Drag/drop areas for splitting - float heightOfVerticalRegion = max(size.y*0.1,(double)FILL_SIZE); - float widthOfVerticalEdge = max(size.x*0.1,(double)FILL_SIZE); - float leftOfMiddle = start.x + widthOfVerticalEdge; - float rightOfMiddle = start.x + size.x - widthOfVerticalEdge; - float topOfMiddle = start.y + (size.y / 2) - heightOfVerticalRegion; - float bottomOfMiddle = start.y + (size.y / 2) + heightOfVerticalRegion;; - float widthOfMiddle = rightOfMiddle - leftOfMiddle; + float dragAreaHeight = max(size.y*0.1,(double)FILL_SIZE); + float dragAreaWidth = max(size.x*0.1,(double)FILL_SIZE); + float middleDragAreaX = start.x + (size.x / 2) - (dragAreaWidth / 2); + float rightDragAreaX = start.x + size.x - dragAreaWidth; + float middleDragAreaY = start.y + (size.y / 2) - (dragAreaHeight / 2); + float bottomDragAreaY = start.y + size.y - dragAreaHeight; + ImVec2 edgeSize(dragAreaWidth, dragAreaHeight); + ImVec2 doubleEdgeSize(dragAreaWidth, 2*dragAreaHeight); bool hit = false; + + if(iArea == 0) { hit |= EdgeDropArea( "top", - ImVec2(leftOfMiddle, start.y), - ImVec2(widthOfMiddle, heightOfVerticalRegion), + ImVec2(middleDragAreaX, start.y), + edgeSize, ImGuiDir_Up); - - topOfMiddle += heightOfVerticalRegion; + if(numAreas != 1) middleDragAreaY += (dragAreaHeight/2); } if(iArea == (numAreas-1)) { - bottomOfMiddle -= heightOfVerticalRegion; hit |= EdgeDropArea( "bottom", - ImVec2(leftOfMiddle, bottomOfMiddle), - ImVec2(widthOfMiddle, heightOfVerticalRegion), + ImVec2(middleDragAreaX, bottomDragAreaY), + edgeSize, ImGuiDir_Down); + if(numAreas != 1) middleDragAreaY -= (dragAreaHeight/2); } - float heightOfMiddle = bottomOfMiddle - topOfMiddle; - - //CenterLeftDropArea(ImVec2(leftOfMiddle, topOfMiddle), ImVec2(widthOfMiddle/2, heightOfMiddle)); + //CenterLeftDropArea(ImVec2(middleDragAreaX, middleDragAreaY), ImVec2(widthOfMiddle/2, heightOfMiddle)); //Only show split in bottom (or top?) area //if( iArea == (numAreas-1) /*|| (iArea == 0)*/ ) - // CenterRightDropArea(ImVec2(leftOfMiddle + widthOfMiddle/2, topOfMiddle), ImVec2(widthOfMiddle/2, heightOfMiddle)); + hit |= CenterRightDropArea(ImVec2(middleDragAreaX + dragAreaWidth, start.y), doubleEdgeSize, ImGuiDir_Up); + hit |= CenterRightDropArea(ImVec2(middleDragAreaX + dragAreaWidth, bottomDragAreaY - dragAreaHeight), doubleEdgeSize, ImGuiDir_Down); - ImVec2 edgeSize(widthOfVerticalEdge, heightOfVerticalRegion); -// ImVec2 edgeSize(widthOfVerticalEdge, heightOfMiddle); - hit |= EdgeDropArea("left", ImVec2(start.x, topOfMiddle), edgeSize, ImGuiDir_Left); - hit |= EdgeDropArea("right", ImVec2(rightOfMiddle, topOfMiddle), edgeSize, ImGuiDir_Right); + hit |= EdgeDropArea("left", ImVec2(start.x, middleDragAreaY), edgeSize, ImGuiDir_Left); + hit |= EdgeDropArea("right", ImVec2(rightDragAreaX, middleDragAreaY), edgeSize, ImGuiDir_Right); if(!hit) CenterDropArea(start, ImVec2(size.x, size.y)); @@ -3815,15 +3814,16 @@ void WaveformArea::CenterDropArea(ImVec2 start, ImVec2 size) StreamGroupDescriptor* streamGroup = *reinterpret_cast(sgpay->Data); //Reject streams not compatible with this plot ok = false; - for(auto channel = streamGroup->m_channels.rbegin(); channel != streamGroup->m_channels.rend(); ++channel) + for(auto channel : streamGroup->m_channels) { - StreamDescriptor s(*channel, 0); + StreamDescriptor s(channel, 0); if(IsCompatible(s) && !IsShowing(s)) { // At least one stream is compatible ok = true; if(payload->IsDelivery()) { AddStream(s,insertionIndex); + insertionIndex++; } } } @@ -3868,94 +3868,15 @@ void WaveformArea::CenterDropArea(ImVec2 start, ImVec2 size) DrawDropRangeMismatchMessage(draw_list, center, GetFirstAnalogStream(), stream); } } - - -/* - ImGui::SetCursorScreenPos(start); - ImGui::InvisibleButton("center", size); - //ImGui::Button("center", size); - ImGui::SetNextItemAllowOverlap(); - - auto payload = ImGui::GetDragDropPayload(); - if(!payload) - return; - bool isWaveform = payload->IsDataType("Waveform"); - if(!isWaveform) - return; - - //Peek the payload. If not compatible, don't even display the target - auto wpay = ImGui::GetDragDropPayload(); - if(!wpay) - return; - - auto desc = reinterpret_cast(wpay->Data); - StreamDescriptor stream = desc->first->GetStream(desc->second); - size_t position = GetStreamPosition(stream); - if(position >= m_displayedChannels.size()) - // Stream not found in this area - return; - - // Round y position to the nearrest space between channel label - ImVec2 mousePos = ImGui::GetMousePos(); - float channelLabeHeight = ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.y; - size_t insertionIndex = (mousePos.y+(channelLabeHeight/2)-start.y) / channelLabeHeight; - insertionIndex = min(insertionIndex,m_displayedChannels.size()); - float insertionYPosition = insertionIndex * channelLabeHeight; - - //Add drop target - bool ok = true; - bool hover = false; - if(ImGui::BeginDragDropTarget()) - { - //Accept drag/drop payloads from this WaveformArea - wpay = ImGui::AcceptDragDropPayload("Waveform", - ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect); - if(wpay) - { - hover = true; - - //Only drop from the same waveform area - if(!(desc->first == this)) - ok = false; - - else if(payload->IsDelivery()) - { // TODO calculate position - MoveStream(stream,insertionIndex); - } - } - ImGui::EndDragDropTarget(); - } - - if(!ok) - { // Add visual feedback if drag source is not compatible with this area - ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); - return; - } - - if(hover) - { - //Draw overlay target - const ImU32 lineColor = ImGui::GetColorU32(ImGuiCol_DockingPreview, 1.00f); - ImVec2 center(start.x + size.x/2, start.y + insertionYPosition); - float fillSize = size.x; - float lineSize = size.x-2; - - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - draw_list->AddLine( - ImVec2(center.x - lineSize/2 - 0.5, center.y), - ImVec2(center.x + lineSize/2 + 0.5, center.y), - lineColor, - 2); - }*/ } /** - @brief Drop area between two waveform areas in a waveform group + @brief Drop area for the middle of the plot - Used to reorder waveform areas within the waveform group + Dropping a waveform in here adds it to a new plot in the same group */ -void WaveformArea::BetweenWaveformsDropArea(ImVec2 start, ImVec2 size) +bool WaveformArea::CenterRightDropArea(ImVec2 start, ImVec2 size, ImGuiDir direction) { ImGui::SetCursorScreenPos(start); ImGui::InvisibleButton("centersplit", size); @@ -3964,27 +3885,21 @@ void WaveformArea::BetweenWaveformsDropArea(ImVec2 start, ImVec2 size) auto payload = ImGui::GetDragDropPayload(); if(!payload) - return; + return false; bool isWaveform = payload->IsDataType("Waveform"); bool isStream = payload->IsDataType("Stream"); bool isStreamGroup = payload->IsDataType("StreamGroup"); if(!isWaveform && !isStream && !isStreamGroup) - return; - - //Peek the payload. If not compatible, don't even display the target - /*auto desc = reinterpret_cast(wpay->Data); - size_t position = m_group->GetAreaPosition(desc->first); - if(position >= m_group->GetWaveformAreas().size()) - // WaveformArea not found in the parent WaveformGroup - return; - StreamDescriptor stream = desc->first->GetStream(desc->second); - */ + return false; //Add drop target StreamDescriptor stream; bool hover = false; if(ImGui::BeginDragDropTarget()) { + size_t waveformAreaPosition = m_group->GetAreaPosition(*this); + if(direction == ImGuiDir_Down) waveformAreaPosition++; + auto wpay = ImGui::AcceptDragDropPayload("Waveform", ImGuiDragDropFlags_AcceptPeekOnly); if(wpay) { @@ -3996,7 +3911,7 @@ void WaveformArea::BetweenWaveformsDropArea(ImVec2 start, ImVec2 size) if( (stream.GetXAxisUnits() == m_group->GetXAxisUnit()) && payload->IsDelivery() ) { auto area = make_shared(stream, m_group, m_parent); - m_group->AddArea(area); + m_group->AddArea(area,waveformAreaPosition); //If the stream is is a density function, propagate the color ramp selection switch(stream.GetType()) @@ -4030,7 +3945,7 @@ void WaveformArea::BetweenWaveformsDropArea(ImVec2 start, ImVec2 size) if( (stream.GetXAxisUnits() == m_group->GetXAxisUnit()) && payload->IsDelivery() ) { auto area = make_shared(stream, m_group, m_parent); - m_group->AddArea(area); + m_group->AddArea(area,waveformAreaPosition); } } @@ -4052,7 +3967,8 @@ void WaveformArea::BetweenWaveformsDropArea(ImVec2 start, ImVec2 size) if(first || !singleArea) { area = make_shared(s, m_group, m_parent); - m_group->AddArea(area); + m_group->AddArea(area,waveformAreaPosition); + waveformAreaPosition++; first = false; } else @@ -4075,143 +3991,19 @@ void WaveformArea::BetweenWaveformsDropArea(ImVec2 start, ImVec2 size) float fillSize = FILL_SIZE; float lineSize = LINE_SIZE; - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - - //Draw background and outline - draw_list->AddRectFilled( - ImVec2(center.x - fillSize/2 - 0.5, center.y - fillSize/2 - 0.5), - ImVec2(center.x + fillSize/2 + 0.5, center.y + fillSize/2 + 0.5), - hover ? bgHovered : bgBase, - rounding); - draw_list->AddRect( - ImVec2(center.x - lineSize/2 - 0.5, center.y - lineSize/2 - 0.5), - ImVec2(center.x + lineSize/2 + 0.5, center.y - 0.5), - lineColor, - rounding); - - draw_list->AddRect( - ImVec2(center.x - lineSize/2 - 0.5, center.y + 0.5), - ImVec2(center.x + lineSize/2 + 0.5, center.y + lineSize/2 + 0.5), - lineColor, - rounding); -} - - -/** - @brief Drop area for the middle of the plot - - Dropping a waveform in here adds it to a new plot in the same group - */ -void WaveformArea::CenterRightDropArea(ImVec2 start, ImVec2 size) -{ - ImGui::SetCursorScreenPos(start); - ImGui::InvisibleButton("centersplit", size); - //ImGui::Button("center", size); - ImGui::SetNextItemAllowOverlap(); - - auto payload = ImGui::GetDragDropPayload(); - if(!payload) - return; - bool isWaveform = payload->IsDataType("Waveform"); - bool isStream = payload->IsDataType("Stream"); - bool isStreamGroup = payload->IsDataType("StreamGroup"); - if(!isWaveform && !isStream && !isStreamGroup) - return; - - //Add drop target - StreamDescriptor stream; - bool hover = false; - if(ImGui::BeginDragDropTarget()) + //Shift center by appropriate direction to be close to the edge + switch(direction) { - auto wpay = ImGui::AcceptDragDropPayload("Waveform", ImGuiDragDropFlags_AcceptPeekOnly); - if(wpay) - { - hover = true; - - auto desc = reinterpret_cast(wpay->Data); - stream = desc->first->GetStream(desc->second); - - if( (stream.GetXAxisUnits() == m_group->GetXAxisUnit()) && payload->IsDelivery() ) - { - auto area = make_shared(stream, m_group, m_parent); - m_group->AddArea(area); - - //If the stream is is a density function, propagate the color ramp selection - switch(stream.GetType()) - { - case Stream::STREAM_TYPE_EYE: - case Stream::STREAM_TYPE_SPECTROGRAM: - case Stream::STREAM_TYPE_WATERFALL: - case Stream::STREAM_TYPE_CONSTELLATION: - { - auto sdc = desc->first->GetDisplayedChannel(desc->second); - area->GetDisplayedChannel(0)->m_colorRamp = sdc->m_colorRamp; - } - break; - - default: - break; - } - - //Remove the stream from the originating waveform area - desc->first->RemoveStream(desc->second); - } - } - - //Accept drag/drop payloads from the stream browser - auto spay = ImGui::AcceptDragDropPayload("Stream", ImGuiDragDropFlags_AcceptPeekOnly); - if(spay) - { - hover = true; - stream = *reinterpret_cast(spay->Data); - - if( (stream.GetXAxisUnits() == m_group->GetXAxisUnit()) && payload->IsDelivery() ) - { - auto area = make_shared(stream, m_group, m_parent); - m_group->AddArea(area); - } - } - - //Accept drag/drop payloads for digital banks - auto sgpay = ImGui::AcceptDragDropPayload("StreamGroup", ImGuiDragDropFlags_AcceptPeekOnly); - if(sgpay) - { - hover = true; - StreamGroupDescriptor* streamGroup = *reinterpret_cast(sgpay->Data); - - if( (streamGroup->GetXAxisUnits() == m_group->GetXAxisUnit()) && payload->IsDelivery() ) - { - std::shared_ptr area; - bool first = true; - bool singleArea = ImGui::IsKeyDown(ImGuiKey_LeftShift)||ImGui::IsKeyDown(ImGuiKey_RightShift); - for(auto channel : streamGroup->m_channels) - { - StreamDescriptor s(channel, 0); - if(first || !singleArea) - { - area = make_shared(s, m_group, m_parent); - m_group->AddArea(area); - first = false; - } - else - { - area->AddStream(s); - } - } - } - } - - ImGui::EndDragDropTarget(); + case ImGuiDir_Up: + center.y = start.y + fillSize/2; + break; + case ImGuiDir_Down: + center.y = start.y + size.y - fillSize/2; + break; + default: + break; } - //Draw overlay target - const float rounding = max(3.0f, ImGui::GetStyle().FrameRounding); - const ImU32 bgBase = ImGui::GetColorU32(ImGuiCol_DockingPreview, 0.70f); - const ImU32 bgHovered = ImGui::GetColorU32(ImGuiCol_DockingPreview, 1.00f); - const ImU32 lineColor = ImGui::GetColorU32(ImGuiCol_NavWindowingHighlight, 0.60f); - ImVec2 center(start.x + size.x/2, start.y + size.y/2); - float fillSize = FILL_SIZE; - float lineSize = LINE_SIZE; ImDrawList* draw_list = ImGui::GetWindowDrawList(); @@ -4232,6 +4024,8 @@ void WaveformArea::CenterRightDropArea(ImVec2 start, ImVec2 size) ImVec2(center.x + lineSize/2 + 0.5, center.y + lineSize/2 + 0.5), lineColor, rounding); + + return hover; } void WaveformArea::DrawDropScalarMessage(ImDrawList* list, ImVec2 center) diff --git a/src/ngscopeclient/WaveformArea.h b/src/ngscopeclient/WaveformArea.h index 9e6a5e42..db66062f 100644 --- a/src/ngscopeclient/WaveformArea.h +++ b/src/ngscopeclient/WaveformArea.h @@ -567,10 +567,9 @@ class WaveformArea : public SerializableObject void DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int numAreas); void CenterLeftDropArea(ImVec2 start, ImVec2 size); - void CenterRightDropArea(ImVec2 start, ImVec2 size); + bool CenterRightDropArea(ImVec2 start, ImVec2 size, ImGuiDir direction); bool EdgeDropArea(const std::string& name, ImVec2 start, ImVec2 size, ImGuiDir splitDir); void CenterDropArea(ImVec2 start, ImVec2 size); - void BetweenWaveformsDropArea(ImVec2 start, ImVec2 size); void FilterMenu(std::shared_ptr chan); void FilterSubmenu(std::shared_ptr chan, const std::string& name, Filter::Category cat); diff --git a/src/ngscopeclient/WaveformGroup.cpp b/src/ngscopeclient/WaveformGroup.cpp index 78b95d6e..291a4475 100644 --- a/src/ngscopeclient/WaveformGroup.cpp +++ b/src/ngscopeclient/WaveformGroup.cpp @@ -102,6 +102,27 @@ void WaveformGroup::AddArea(shared_ptr& area) m_parent->RefreshStreamBrowserDialog(); } +void WaveformGroup::AddArea(shared_ptr& area, size_t position) +{ + lock_guard lock(m_areaMutex); + { + //If this is our first area, adopt its X axis unit as our own + if(m_areas.empty()) + m_xAxisUnit = area->GetStream(0).GetXAxisUnits(); + + if(position >= m_areas.size()) + { + m_areas.push_back(area); + } + else + { + m_areas.insert(m_areas.begin() + position, area); + } + } + + m_parent->RefreshStreamBrowserDialog(); +} + /** * Get the position of the provided WavformArea in this waveform group * @param area the area to get the position for diff --git a/src/ngscopeclient/WaveformGroup.h b/src/ngscopeclient/WaveformGroup.h index 7ea02caa..e6b77a20 100644 --- a/src/ngscopeclient/WaveformGroup.h +++ b/src/ngscopeclient/WaveformGroup.h @@ -68,6 +68,8 @@ class WaveformGroup : public SerializableObject void AddArea(std::shared_ptr& area); + void AddArea(std::shared_ptr& area, size_t position); + size_t GetAreaPosition(WaveformArea& area); void MoveArea(WaveformArea& area, size_t newPosition); From 0e1e96f7f5c708a10c6de8923aa6a5fe2f248d7e Mon Sep 17 00:00:00 2001 From: Frederic Borry Date: Sun, 15 Feb 2026 00:54:20 +0100 Subject: [PATCH 6/9] Fixed channel area reordering. --- src/ngscopeclient/WaveformArea.cpp | 8 ++------ src/ngscopeclient/WaveformGroup.cpp | 17 ++--------------- src/ngscopeclient/WaveformGroup.h | 2 +- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/src/ngscopeclient/WaveformArea.cpp b/src/ngscopeclient/WaveformArea.cpp index 351c3f80..743f7a49 100644 --- a/src/ngscopeclient/WaveformArea.cpp +++ b/src/ngscopeclient/WaveformArea.cpp @@ -3331,8 +3331,8 @@ void WaveformArea::RenderBERLevelArrows(ImVec2 start, ImVec2 /*size*/) void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int numAreas) { //Drag/drop areas for splitting - float dragAreaHeight = max(size.y*0.1,(double)FILL_SIZE); - float dragAreaWidth = max(size.x*0.1,(double)FILL_SIZE); + float dragAreaHeight = size.y*0.1; + float dragAreaWidth = size.x*0.1; float middleDragAreaX = start.x + (size.x / 2) - (dragAreaWidth / 2); float rightDragAreaX = start.x + size.x - dragAreaWidth; float middleDragAreaY = start.y + (size.y / 2) - (dragAreaHeight / 2); @@ -3362,10 +3362,6 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu if(numAreas != 1) middleDragAreaY -= (dragAreaHeight/2); } - //CenterLeftDropArea(ImVec2(middleDragAreaX, middleDragAreaY), ImVec2(widthOfMiddle/2, heightOfMiddle)); - - //Only show split in bottom (or top?) area - //if( iArea == (numAreas-1) /*|| (iArea == 0)*/ ) hit |= CenterRightDropArea(ImVec2(middleDragAreaX + dragAreaWidth, start.y), doubleEdgeSize, ImGuiDir_Up); hit |= CenterRightDropArea(ImVec2(middleDragAreaX + dragAreaWidth, bottomDragAreaY - dragAreaHeight), doubleEdgeSize, ImGuiDir_Down); diff --git a/src/ngscopeclient/WaveformGroup.cpp b/src/ngscopeclient/WaveformGroup.cpp index 291a4475..ff7035b5 100644 --- a/src/ngscopeclient/WaveformGroup.cpp +++ b/src/ngscopeclient/WaveformGroup.cpp @@ -159,19 +159,6 @@ void WaveformGroup::MoveArea(WaveformArea& area, size_t newPosition) // Nothing to do return; -/* if (oldIndex < newPosition) - { - std::rotate(m_areas.begin() + oldIndex, - m_areas.begin() + oldIndex + 1, - m_areas.begin() + newPosition + 1); - } - else - { - std::rotate(m_areas.begin() + newPosition, - m_areas.begin() + oldIndex, - m_areas.begin() + oldIndex + 1); - }*/ - // Backup value std::shared_ptr temp = m_areas[oldIndex]; @@ -304,10 +291,10 @@ bool WaveformGroup::Render() for(size_t i=0; iRender(i, areas.size(), clientArea)) - m_areasToClose.push_back(i); + m_areasToClose.push_back(areas[i]); } for(ssize_t i=static_cast(m_areasToClose.size()) - 1; i >= 0; i--) - m_areas.erase(m_areas.begin() + m_areasToClose[i]); + m_areas.erase(std::remove(m_areas.begin(), m_areas.end(), m_areasToClose[i]), m_areas.end()); if(!m_areasToClose.empty()) m_parent->RefreshStreamBrowserDialog(); diff --git a/src/ngscopeclient/WaveformGroup.h b/src/ngscopeclient/WaveformGroup.h index e6b77a20..c7923361 100644 --- a/src/ngscopeclient/WaveformGroup.h +++ b/src/ngscopeclient/WaveformGroup.h @@ -212,7 +212,7 @@ class WaveformGroup : public SerializableObject double m_tLastMouseMove; ///@brief List of waveform areas to close next frame - std::vector m_areasToClose; + std::vector< std::shared_ptr > m_areasToClose; ///@brief Height of the timeline float m_timelineHeight; From 6977e2ebffff5f26f6a8e7ed140b81f7ae06687f Mon Sep 17 00:00:00 2001 From: Frederic Borry Date: Sun, 15 Feb 2026 11:28:33 +0100 Subject: [PATCH 7/9] More code cleanup. Fixed drag area size for small height. --- src/ngscopeclient/WaveformArea.cpp | 205 ++++------------------------- src/ngscopeclient/WaveformArea.h | 1 - 2 files changed, 28 insertions(+), 178 deletions(-) diff --git a/src/ngscopeclient/WaveformArea.cpp b/src/ngscopeclient/WaveformArea.cpp index 743f7a49..2b8c64e2 100644 --- a/src/ngscopeclient/WaveformArea.cpp +++ b/src/ngscopeclient/WaveformArea.cpp @@ -3330,40 +3330,44 @@ void WaveformArea::RenderBERLevelArrows(ImVec2 start, ImVec2 /*size*/) */ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int numAreas) { + bool isFirst = (iArea == 0); + bool isLast = (iArea == (numAreas-1)); //Drag/drop areas for splitting - float dragAreaHeight = size.y*0.1; - float dragAreaWidth = size.x*0.1; - float middleDragAreaX = start.x + (size.x / 2) - (dragAreaWidth / 2); + float dragAreaWidth = max((double)size.x*0.1,(double)FILL_SIZE); + float dragAreaHeight = max((double)size.y*0.1,(double)FILL_SIZE); + // Make sure we have room for two split drag area overlays (needed for last row) + float splitDragAreaHeight = min(max((double)size.y*0.1,(double)FILL_SIZE),(double)size.y/2); + float middleDragAreaX = start.x + (size.x/2) - (dragAreaWidth/2); float rightDragAreaX = start.x + size.x - dragAreaWidth; - float middleDragAreaY = start.y + (size.y / 2) - (dragAreaHeight / 2); + float middleDragAreaY = start.y + (size.y/2) - (dragAreaHeight/2); float bottomDragAreaY = start.y + size.y - dragAreaHeight; + float bottomSplitDragAreaY = start.y + size.y - splitDragAreaHeight; ImVec2 edgeSize(dragAreaWidth, dragAreaHeight); - ImVec2 doubleEdgeSize(dragAreaWidth, 2*dragAreaHeight); + ImVec2 edgeSizeSplit(dragAreaWidth, splitDragAreaHeight); bool hit = false; - - if(iArea == 0) + if(isFirst) { hit |= EdgeDropArea( "top", ImVec2(middleDragAreaX, start.y), edgeSize, ImGuiDir_Up); - if(numAreas != 1) middleDragAreaY += (dragAreaHeight/2); + //if(numAreas != 1) middleDragAreaY += (dragAreaHeight/2); } - if(iArea == (numAreas-1)) + if(isLast) { hit |= EdgeDropArea( "bottom", ImVec2(middleDragAreaX, bottomDragAreaY), edgeSize, ImGuiDir_Down); - if(numAreas != 1) middleDragAreaY -= (dragAreaHeight/2); + //if(numAreas != 1) middleDragAreaY -= (dragAreaHeight/2); } - hit |= CenterRightDropArea(ImVec2(middleDragAreaX + dragAreaWidth, start.y), doubleEdgeSize, ImGuiDir_Up); - hit |= CenterRightDropArea(ImVec2(middleDragAreaX + dragAreaWidth, bottomDragAreaY - dragAreaHeight), doubleEdgeSize, ImGuiDir_Down); + hit |= CenterRightDropArea(ImVec2(middleDragAreaX + dragAreaWidth, start.y), edgeSizeSplit, ImGuiDir_Up); + if(isLast) hit |= CenterRightDropArea(ImVec2(middleDragAreaX + dragAreaWidth, bottomSplitDragAreaY), edgeSizeSplit, ImGuiDir_Down); hit |= EdgeDropArea("left", ImVec2(start.x, middleDragAreaY), edgeSize, ImGuiDir_Left); hit |= EdgeDropArea("right", ImVec2(rightDragAreaX, middleDragAreaY), edgeSize, ImGuiDir_Right); @@ -3543,161 +3547,6 @@ bool WaveformArea::EdgeDropArea(const string& name, ImVec2 start, ImVec2 size, I return hover; } -/** - @brief Drop area for the middle of the plot - - Dropping a waveform in here adds it to the plot - */ -void WaveformArea::CenterLeftDropArea(ImVec2 start, ImVec2 size) -{ - ImGui::SetCursorScreenPos(start); - ImGui::InvisibleButton("center", size); - //ImGui::Button("center", size); - ImGui::SetNextItemAllowOverlap(); - - auto payload = ImGui::GetDragDropPayload(); - if(!payload) - return; - bool isWaveform = payload->IsDataType("Waveform"); - bool isStream = payload->IsDataType("Stream"); - bool isStreamGroup = payload->IsDataType("StreamGroup"); - if(!isWaveform && !isStream &&!isStreamGroup) - return; - - //Peek the payload. If not compatible, don't even display the target - StreamDescriptor stream; - auto peekPayload = ImGui::GetDragDropPayload(); - if(peekPayload) - { - if(isWaveform) - { - auto peekDesc = reinterpret_cast(peekPayload->Data); - stream = peekDesc->first->GetStream(peekDesc->second); - if( (peekDesc->first == this) || !IsCompatible(stream)) - return; - } - else if(isStream) - { - stream = *reinterpret_cast(peekPayload->Data); - if(!IsCompatible(stream)) - return; - } - } - - //Add drop target - bool ok = true; - bool hover = false; - if(ImGui::BeginDragDropTarget()) - { - //Accept drag/drop payloads from another WaveformArea - auto wpay = ImGui::AcceptDragDropPayload("Waveform", - ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect); - if(wpay) - { - hover = true; - - auto desc = reinterpret_cast(wpay->Data); - stream = desc->first->GetStream(desc->second); - - //Don't allow dropping in the same area - //Reject streams not compatible with this plot - if( (desc->first == this) || !IsCompatible(stream) ||IsShowing(stream)) - ok = false; - - else if(payload->IsDelivery()) - { - //Add the new stream to us - //TODO: copy view settings from the DisplayedChannel over? - AddStream(stream); - - //Remove the stream from the originating waveform area - desc->first->RemoveStream(desc->second); - } - } - - //Accept drag/drop payloads from the stream browser - auto spay = ImGui::AcceptDragDropPayload("Stream", - ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect); - if(spay) - { - hover = true; - stream = *reinterpret_cast(spay->Data); - - //Reject streams not compatible with this plot - if(!IsCompatible(stream)||IsShowing(stream)) - ok = false; - - else if(payload->IsDelivery()) - AddStream(stream); - } - //Accept stream group - auto sgpay = ImGui::AcceptDragDropPayload("StreamGroup", - ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect); - if(sgpay) - { - hover = true; - StreamGroupDescriptor* streamGroup = *reinterpret_cast(sgpay->Data); - //Reject streams not compatible with this plot - ok = false; - for(auto channel : streamGroup->m_channels) - { - StreamDescriptor s(channel, 0); - if(IsCompatible(s) && !IsShowing(s)) - { // At least one stream is compatible - ok = true; - if(payload->IsDelivery()) - { - AddStream(s); - } - } - } - } - - ImGui::EndDragDropTarget(); - } - - if(!ok) - { // Add visual feedback if drag source is not compatible with this area - ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); - return; - } - - //Draw overlay target - const float rounding = max(3.0f, ImGui::GetStyle().FrameRounding); - const ImU32 bgBase = ImGui::GetColorU32(ImGuiCol_DockingPreview, 0.70f); - const ImU32 bgHovered = ImGui::GetColorU32(ImGuiCol_DockingPreview, 1.00f); - const ImU32 lineColor = ImGui::GetColorU32(ImGuiCol_NavWindowingHighlight, 0.60f); - ImVec2 center(start.x + size.x/2, start.y + size.y/2); - float fillSize = FILL_SIZE; - float lineSize = LINE_SIZE; - - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - draw_list->AddRectFilled( - ImVec2(center.x - fillSize/2 - 0.5, center.y - fillSize/2 - 0.5), - ImVec2(center.x + fillSize/2 + 0.5, center.y + fillSize/2 + 0.5), - hover ? bgHovered : bgBase, - rounding); - draw_list->AddRect( - ImVec2(center.x - lineSize/2 - 0.5, center.y - lineSize/2 - 0.5), - ImVec2(center.x + lineSize/2 + 0.5, center.y + lineSize/2 + 0.5), - lineColor, - rounding); - - //If trying to drop into the center marker, display warning if incompatible scales - //(new signal has significantly wider range) and not a filter - if(hover) - { - if(stream.GetType() != Stream::STREAM_TYPE_ANALOG) - return; - auto chan = stream.m_channel; - if(dynamic_cast(chan) != nullptr) - return; - - center.x += fillSize; - DrawDropRangeMismatchMessage(draw_list, center, GetFirstAnalogStream(), stream); - } -} - /** @brief Drop area between two waveform areas in a waveform group @@ -3984,17 +3833,19 @@ bool WaveformArea::CenterRightDropArea(ImVec2 start, ImVec2 size, ImGuiDir direc const ImU32 bgHovered = ImGui::GetColorU32(ImGuiCol_DockingPreview, 1.00f); const ImU32 lineColor = ImGui::GetColorU32(ImGuiCol_NavWindowingHighlight, 0.60f); ImVec2 center(start.x + size.x/2, start.y + size.y/2); - float fillSize = FILL_SIZE; - float lineSize = LINE_SIZE; + float fillWidth = FILL_SIZE; + float lineWidth = LINE_SIZE; + float fillHeigth = min((double)FILL_SIZE,(double)size.y); + float lineHeigth = fillHeigth-2; //Shift center by appropriate direction to be close to the edge switch(direction) { case ImGuiDir_Up: - center.y = start.y + fillSize/2; + center.y = start.y + fillHeigth/2; break; case ImGuiDir_Down: - center.y = start.y + size.y - fillSize/2; + center.y = start.y + size.y - fillHeigth/2; break; default: break; @@ -4005,19 +3856,19 @@ bool WaveformArea::CenterRightDropArea(ImVec2 start, ImVec2 size, ImGuiDir direc //Draw background and outline draw_list->AddRectFilled( - ImVec2(center.x - fillSize/2 - 0.5, center.y - fillSize/2 - 0.5), - ImVec2(center.x + fillSize/2 + 0.5, center.y + fillSize/2 + 0.5), + ImVec2(center.x - fillWidth/2 - 0.5, center.y - fillHeigth/2 - 0.5), + ImVec2(center.x + fillWidth/2 + 0.5, center.y + fillHeigth/2 + 0.5), hover ? bgHovered : bgBase, rounding); draw_list->AddRect( - ImVec2(center.x - lineSize/2 - 0.5, center.y - lineSize/2 - 0.5), - ImVec2(center.x + lineSize/2 + 0.5, center.y - 0.5), + ImVec2(center.x - lineWidth/2 - 0.5, center.y - lineHeigth/2 - 0.5), + ImVec2(center.x + lineWidth/2 + 0.5, center.y - 0.5), lineColor, rounding); draw_list->AddRect( - ImVec2(center.x - lineSize/2 - 0.5, center.y + 0.5), - ImVec2(center.x + lineSize/2 + 0.5, center.y + lineSize/2 + 0.5), + ImVec2(center.x - lineWidth/2 - 0.5, center.y + 0.5), + ImVec2(center.x + lineWidth/2 + 0.5, center.y + lineHeigth/2 + 0.5), lineColor, rounding); diff --git a/src/ngscopeclient/WaveformArea.h b/src/ngscopeclient/WaveformArea.h index db66062f..9c81daef 100644 --- a/src/ngscopeclient/WaveformArea.h +++ b/src/ngscopeclient/WaveformArea.h @@ -566,7 +566,6 @@ class WaveformArea : public SerializableObject void DrawDropScalarMessage(ImDrawList* list, ImVec2 center); void DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int numAreas); - void CenterLeftDropArea(ImVec2 start, ImVec2 size); bool CenterRightDropArea(ImVec2 start, ImVec2 size, ImGuiDir direction); bool EdgeDropArea(const std::string& name, ImVec2 start, ImVec2 size, ImGuiDir splitDir); void CenterDropArea(ImVec2 start, ImVec2 size); From ddd35406567a6ff43a435ff918bde853436e3515 Mon Sep 17 00:00:00 2001 From: Frederic Borry Date: Sun, 15 Feb 2026 11:35:21 +0100 Subject: [PATCH 8/9] More code cleanup. --- src/ngscopeclient/WaveformArea.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ngscopeclient/WaveformArea.cpp b/src/ngscopeclient/WaveformArea.cpp index 2b8c64e2..79aaf2f3 100644 --- a/src/ngscopeclient/WaveformArea.cpp +++ b/src/ngscopeclient/WaveformArea.cpp @@ -3353,7 +3353,6 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu ImVec2(middleDragAreaX, start.y), edgeSize, ImGuiDir_Up); - //if(numAreas != 1) middleDragAreaY += (dragAreaHeight/2); } if(isLast) @@ -3363,7 +3362,6 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu ImVec2(middleDragAreaX, bottomDragAreaY), edgeSize, ImGuiDir_Down); - //if(numAreas != 1) middleDragAreaY -= (dragAreaHeight/2); } hit |= CenterRightDropArea(ImVec2(middleDragAreaX + dragAreaWidth, start.y), edgeSizeSplit, ImGuiDir_Up); @@ -3372,8 +3370,7 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu hit |= EdgeDropArea("left", ImVec2(start.x, middleDragAreaY), edgeSize, ImGuiDir_Left); hit |= EdgeDropArea("right", ImVec2(rightDragAreaX, middleDragAreaY), edgeSize, ImGuiDir_Right); - if(!hit) - CenterDropArea(start, ImVec2(size.x, size.y)); + if(!hit) CenterDropArea(start, ImVec2(size.x, size.y)); //Cannot drop scalars into a waveform view. Make this a bit more obvious auto payload = ImGui::GetDragDropPayload(); From 6db1419fe3f8fa959f9d47ad7f01e9e237110bbf Mon Sep 17 00:00:00 2001 From: fredzo Date: Sun, 15 Feb 2026 15:51:00 +0100 Subject: [PATCH 9/9] Fixed comments. Fixed path for AddInstrumentDIalog on connection error. --- src/ngscopeclient/AddInstrumentDialog.cpp | 2 +- src/ngscopeclient/WaveformArea.cpp | 27 +++++++++++++---------- src/ngscopeclient/WaveformGroup.cpp | 10 ++++++++- src/ngscopeclient/WaveformGroup.h | 2 +- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/ngscopeclient/AddInstrumentDialog.cpp b/src/ngscopeclient/AddInstrumentDialog.cpp index 99d8ab59..de471dea 100644 --- a/src/ngscopeclient/AddInstrumentDialog.cpp +++ b/src/ngscopeclient/AddInstrumentDialog.cpp @@ -67,7 +67,7 @@ AddInstrumentDialog::AddInstrumentDialog( { SCPITransport::EnumTransports(m_transports); m_supportedTransports.insert(m_transports.begin(), m_transports.end()); - m_pathEdited = false; + m_pathEdited = !m_path.empty(); m_defaultNickname = nickname; m_originalNickname = nickname; m_nicknameEdited = false; diff --git a/src/ngscopeclient/WaveformArea.cpp b/src/ngscopeclient/WaveformArea.cpp index 79aaf2f3..2c277898 100644 --- a/src/ngscopeclient/WaveformArea.cpp +++ b/src/ngscopeclient/WaveformArea.cpp @@ -448,7 +448,7 @@ void WaveformArea::AddStream(StreamDescriptor desc, bool persistence, const stri } /** - @brief Adds a new stream to this plot at given position + @brief Adds a new stream to this plot at a given position */ void WaveformArea::AddStream(StreamDescriptor desc, size_t position, bool persistence, const string& ramp) { @@ -468,7 +468,7 @@ void WaveformArea::AddStream(StreamDescriptor desc, size_t position, bool persis /** * Get the position of the provided Stream in this WaveformArea * @param desc the stream to get the position for - * @return the position of the stream if found or the number of channel in this WaveformArea otherwise + * @return the position of the stream if found or the number of displayed channels in this WaveformArea otherwise */ size_t WaveformArea::GetStreamPosition(StreamDescriptor desc) { @@ -485,7 +485,9 @@ size_t WaveformArea::GetStreamPosition(StreamDescriptor desc) } /** - @brief Adds a new stream to this plot + @brief Move a stream to another position in this plot + @param desc the stream to move + @param newPosition the position to move the stream to */ void WaveformArea::MoveStream(StreamDescriptor desc, size_t newPosition) { @@ -3332,7 +3334,8 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu { bool isFirst = (iArea == 0); bool isLast = (iArea == (numAreas-1)); - //Drag/drop areas for splitting + // Drag/drop areas size and positions : we keep 10% of the waveform area for edge ans split drop areas, + // the rest is used for dropping/moving wavefroms at a specific postion in the waveform area float dragAreaWidth = max((double)size.x*0.1,(double)FILL_SIZE); float dragAreaHeight = max((double)size.y*0.1,(double)FILL_SIZE); // Make sure we have room for two split drag area overlays (needed for last row) @@ -3343,11 +3346,12 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu float bottomDragAreaY = start.y + size.y - dragAreaHeight; float bottomSplitDragAreaY = start.y + size.y - splitDragAreaHeight; ImVec2 edgeSize(dragAreaWidth, dragAreaHeight); + // Height for split area may be reduced to fit available space in waveform area ImVec2 edgeSizeSplit(dragAreaWidth, splitDragAreaHeight); bool hit = false; if(isFirst) - { + { // Add top drop area to first waveform area of the group hit |= EdgeDropArea( "top", ImVec2(middleDragAreaX, start.y), @@ -3356,7 +3360,7 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu } if(isLast) - { + { // Add bottom drop area to last waveform area of the group hit |= EdgeDropArea( "bottom", ImVec2(middleDragAreaX, bottomDragAreaY), @@ -3364,12 +3368,15 @@ void WaveformArea::DragDropOverlays(ImVec2 start, ImVec2 size, int iArea, int nu ImGuiDir_Down); } + // Add split drop area at the top of each and every waveform area of the waveform group hit |= CenterRightDropArea(ImVec2(middleDragAreaX + dragAreaWidth, start.y), edgeSizeSplit, ImGuiDir_Up); + // Only add bottom split drop area to the last waveform area of a waveform group if(isLast) hit |= CenterRightDropArea(ImVec2(middleDragAreaX + dragAreaWidth, bottomSplitDragAreaY), edgeSizeSplit, ImGuiDir_Down); hit |= EdgeDropArea("left", ImVec2(start.x, middleDragAreaY), edgeSize, ImGuiDir_Left); hit |= EdgeDropArea("right", ImVec2(rightDragAreaX, middleDragAreaY), edgeSize, ImGuiDir_Right); + // If we've hit any of other drop areas, process center drop area if(!hit) CenterDropArea(start, ImVec2(size.x, size.y)); //Cannot drop scalars into a waveform view. Make this a bit more obvious @@ -3545,15 +3552,12 @@ bool WaveformArea::EdgeDropArea(const string& name, ImVec2 start, ImVec2 size, I } /** - @brief Drop area between two waveform areas in a waveform group - - Used to reorder waveform areas within the waveform group + @brief Main drop area used to drop a stream in this waveform area at a given position */ void WaveformArea::CenterDropArea(ImVec2 start, ImVec2 size) { ImGui::SetCursorScreenPos(start); ImGui::InvisibleButton("center", size); - //ImGui::Button("center", size); ImGui::SetNextItemAllowOverlap(); auto payload = ImGui::GetDragDropPayload(); @@ -3680,10 +3684,9 @@ void WaveformArea::CenterDropArea(ImVec2 start, ImVec2 size) return; } - //Draw overlay target if(hover) { - //Draw overlay target + //Draw an horizontal line at the insertion position const ImU32 lineColor = ImGui::GetColorU32(ImGuiCol_DockingPreview, 1.00f); ImVec2 center(start.x + size.x/2, start.y + insertionYPosition); float fillSize = size.x; diff --git a/src/ngscopeclient/WaveformGroup.cpp b/src/ngscopeclient/WaveformGroup.cpp index ff7035b5..e29ceeca 100644 --- a/src/ngscopeclient/WaveformGroup.cpp +++ b/src/ngscopeclient/WaveformGroup.cpp @@ -2,7 +2,7 @@ * * * ngscopeclient * * * -* Copyright (c) 2012-2025 Andrew D. Zonenberg and contributors * +* Copyright (c) 2012-2026 Andrew D. Zonenberg and contributors * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * @@ -102,6 +102,9 @@ void WaveformGroup::AddArea(shared_ptr& area) m_parent->RefreshStreamBrowserDialog(); } +/** + @brief Adds a new area to this group at a given position + */ void WaveformGroup::AddArea(shared_ptr& area, size_t position) { lock_guard lock(m_areaMutex); @@ -145,6 +148,11 @@ size_t WaveformGroup::GetAreaPosition(WaveformArea& area) } } +/** + @brief Move a waveform are to another position in this group + @param area the waveform area to move + @param newPosition the position to move the waveform area to + */ void WaveformGroup::MoveArea(WaveformArea& area, size_t newPosition) { lock_guard lock(m_areaMutex); diff --git a/src/ngscopeclient/WaveformGroup.h b/src/ngscopeclient/WaveformGroup.h index c7923361..1b313692 100644 --- a/src/ngscopeclient/WaveformGroup.h +++ b/src/ngscopeclient/WaveformGroup.h @@ -2,7 +2,7 @@ * * * ngscopeclient * * * -* Copyright (c) 2012-2025 Andrew D. Zonenberg and contributors * +* Copyright (c) 2012-2026 Andrew D. Zonenberg and contributors * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *