Allow drag-drop for schematic elements

Dragging screen elements over a subsheet allows moving elements into a
subsheet
This commit is contained in:
Seth Hillbrand 2025-09-11 02:15:31 -07:00
parent 3b97804cb6
commit dcbadb5857
2 changed files with 138 additions and 2 deletions

View File

@ -46,6 +46,9 @@
#include <pgm_base.h>
#include <view/view_controls.h>
#include <settings/settings_manager.h>
#include <math/box2.h>
#include <base_units.h>
#include <sch_screen.h>
#include "sch_move_tool.h"
@ -500,6 +503,7 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm
TOOL_EVENT* evt = &copy;
VECTOR2I prevPos;
GRID_HELPER_GRIDS snapLayer = GRID_CURRENT;
SCH_SHEET* hoverSheet = nullptr;
m_cursor = controls->GetCursorPosition();
@ -771,7 +775,65 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm
m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ),
snapLayer, selection );
// Determine potential target sheet.
SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( m_frame->GetScreen()->GetItem( m_cursor, 0,
SCH_SHEET_T ) );
if( sheet && sheet->IsSelected() )
sheet = nullptr; // Never target a selected sheet
if( !sheet )
{
// Build current selection bounding box in its (already moved) position.
BOX2I selBBox;
for( EDA_ITEM* it : selection )
{
if( SCH_ITEM* schIt = dynamic_cast<SCH_ITEM*>( it ) )
selBBox.Merge( schIt->GetBoundingBox() );
}
if( selBBox.GetWidth() > 0 && selBBox.GetHeight() > 0 )
{
VECTOR2I selCenter( selBBox.GetX() + selBBox.GetWidth() / 2,
selBBox.GetY() + selBBox.GetHeight() / 2 );
// Find first non-selected sheet whose body fully contains the selection
// or at least contains its center point.
for( SCH_ITEM* it : m_frame->GetScreen()->Items().OfType( SCH_SHEET_T ) )
{
SCH_SHEET* candidate = static_cast<SCH_SHEET*>( it );
if( candidate->IsSelected() || candidate->IsRootSheet() )
continue;
BOX2I body = candidate->GetBodyBoundingBox();
if( body.Contains( selBBox ) || body.Contains( selCenter ) )
{
sheet = candidate;
break;
}
}
}
}
if( sheet != hoverSheet )
{
if( hoverSheet )
{
hoverSheet->ClearFlags( BRIGHTENED );
m_frame->UpdateItem( hoverSheet, false );
}
hoverSheet = sheet;
if( hoverSheet )
{
hoverSheet->SetFlags( BRIGHTENED );
m_frame->UpdateItem( hoverSheet, false );
}
}
m_frame->GetCanvas()->SetCurrentCursor( hoverSheet ? KICURSOR::PLACE
: KICURSOR::MOVING );
VECTOR2I delta( m_cursor - prevPos );
m_anchorPos = m_cursor;
@ -1032,6 +1094,22 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm
} while( ( evt = Wait() ) ); //Should be assignment not equality test
SCH_SHEET* targetSheet = hoverSheet;
if( hoverSheet )
{
hoverSheet->ClearFlags( BRIGHTENED );
m_frame->UpdateItem( hoverSheet, false );
}
if( targetSheet )
{
moveSelectionToSheet( selection, targetSheet, aCommit );
m_toolMgr->RunAction( ACTIONS::selectionClear );
m_newDragLines.clear();
m_changedDragLines.clear();
}
// Create a selection of original selection, drag selected/changed items, and new
// bend lines for later before we clear them in the aCommit. We'll need these
// to check for new junctions needed, etc.
@ -1135,6 +1213,60 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm
}
void SCH_MOVE_TOOL::moveSelectionToSheet( SCH_SELECTION& aSelection, SCH_SHEET* aTargetSheet,
SCH_COMMIT* aCommit )
{
SCH_SCREEN* destScreen = aTargetSheet->GetScreen();
SCH_SCREEN* srcScreen = m_frame->GetScreen();
BOX2I bbox;
for( EDA_ITEM* item : aSelection )
bbox.Merge( static_cast<SCH_ITEM*>( item )->GetBoundingBox() );
VECTOR2I offset = VECTOR2I( 0, 0 ) - bbox.GetPosition();
int step = schIUScale.MilsToIU( 50 );
bool overlap = false;
do
{
BOX2I moved = bbox;
moved.Move( offset );
overlap = false;
for( SCH_ITEM* existing : destScreen->Items() )
{
if( moved.Intersects( existing->GetBoundingBox() ) )
{
overlap = true;
break;
}
}
if( overlap )
offset += VECTOR2I( step, step );
} while( overlap );
for( EDA_ITEM* item : aSelection )
{
SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
// Remove from current screen and view manually
m_frame->RemoveFromScreen( schItem, srcScreen );
// Move the item
schItem->Move( offset );
// Add to destination screen manually (won't add to view since it's not current)
destScreen->Append( schItem );
// Record in commit with CHT_DONE flag to bypass automatic screen/view operations
aCommit->Stage( schItem, CHT_REMOVE | CHT_DONE, srcScreen );
aCommit->Stage( schItem, CHT_ADD | CHT_DONE, destScreen );
}
}
void SCH_MOVE_TOOL::trimDanglingLines( SCH_COMMIT* aCommit )
{
// Need a local cleanup first to ensure we remove unneeded junctions
@ -1163,8 +1295,7 @@ void SCH_MOVE_TOOL::trimDanglingLines( SCH_COMMIT* aCommit )
{
line->SetFlags( STRUCT_DELETED );
aCommit->Removed( line, m_frame->GetScreen() );
updateItem( line, false );
updateItem( line, false ); // Update any cached visuals before commit processes
m_frame->RemoveFromScreen( line, m_frame->GetScreen() );
}
}

View File

@ -35,6 +35,9 @@ class SCH_LINE;
class SCH_LABEL_BASE;
class SCH_SHEET_PIN;
class SCH_JUNCTION;
class SCH_SELECTION;
class SCH_SHEET;
class SCH_COMMIT;
struct SPECIAL_CASE_LABEL_INFO
@ -81,6 +84,8 @@ private:
void orthoLineDrag( SCH_COMMIT* aCommit, SCH_LINE* line, const VECTOR2I& splitDelta,
int& xBendCount, int& yBendCount, const EE_GRID_HELPER& grid );
void moveSelectionToSheet( SCH_SELECTION& aSelection, SCH_SHEET* aTarget, SCH_COMMIT* aCommit );
///< Clears the new drag lines and removes them from the screen
void clearNewDragLines();