Overhaul drag-select logic to handle children better.

Fixes https://gitlab.com/kicad/code/kicad/issues/6662
This commit is contained in:
Jeff Young 2021-12-10 18:43:35 +00:00
parent 84e0feeee6
commit 0ad0627bb0
1 changed files with 52 additions and 62 deletions

View File

@ -1184,18 +1184,19 @@ bool EE_SELECTION_TOOL::selectMultiple()
while( TOOL_EVENT* evt = Wait() ) while( TOOL_EVENT* evt = Wait() )
{ {
int width = area.GetEnd().x - area.GetOrigin().x; int width = area.GetEnd().x - area.GetOrigin().x;
int height = area.GetEnd().y - area.GetOrigin().y;
/* Selection mode depends on direction of drag-selection: /* Selection mode depends on direction of drag-selection:
* Left > Right : Select objects that are fully enclosed by selection * Left > Right : Select objects that are fully enclosed by selection
* Right > Left : Select objects that are crossed by selection * Right > Left : Select objects that are crossed by selection
*/ */
bool windowSelection = width >= 0; bool isWindowSelection = width >= 0;
if( view->IsMirroredX() ) if( view->IsMirroredX() )
windowSelection = !windowSelection; isWindowSelection = !isWindowSelection;
m_frame->GetCanvas()->SetCurrentCursor( windowSelection ? KICURSOR::SELECT_WINDOW m_frame->GetCanvas()->SetCurrentCursor( isWindowSelection ? KICURSOR::SELECT_WINDOW
: KICURSOR::SELECT_LASSO ); : KICURSOR::SELECT_LASSO );
if( evt->IsCancelInteractive() || evt->IsActivate() ) if( evt->IsCancelInteractive() || evt->IsActivate() )
{ {
@ -1227,79 +1228,68 @@ bool EE_SELECTION_TOOL::selectMultiple()
// End drawing the selection box // End drawing the selection box
view->SetVisible( &area, false ); view->SetVisible( &area, false );
// Mark items within the selection box as selected // Fetch items from the RTree that are in our area of interest
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems; std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> nearbyViewItems;
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> children; view->Query( area.ViewBBox(), nearbyViewItems );
// Filter the view items based on the selection box // Build lists of nearby items and their children
BOX2I selectionBox = area.ViewBBox(); std::vector<SCH_ITEM*> nearbyItems;
view->Query( selectionBox, selectedItems ); // Get the list of selected items std::vector<SCH_ITEM*> nearbyChildren;
// Some children aren't in the view; add them by hand. for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : nearbyViewItems )
// DO NOT add them directly to selectedItems. If we add enough to cause the vector
// to grow it will re-allocate and invalidate the top-level for-loop iterator.
for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
{ {
SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( pair.first ); SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( pair.first );
if( sheet ) if( item )
{ {
int layer = pair.second; item->ClearFlags( TEMP_SELECTED | STARTPOINT | ENDPOINT );
nearbyItems.push_back( item );
for( SCH_SHEET_PIN* pin : sheet->GetPins() ) item->RunOnChildren(
children.emplace_back( KIGFX::VIEW::LAYER_ITEM_PAIR( pin, layer ) ); [&]( SCH_ITEM* aChild )
} {
nearbyChildren.push_back( aChild );
SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pair.first ); } );
if( symbol )
{
int layer = pair.second;
for( SCH_FIELD& field : symbol->GetFields() )
children.emplace_back( KIGFX::VIEW::LAYER_ITEM_PAIR( &field, layer ) );
}
SCH_GLOBALLABEL* gLabel = dynamic_cast<SCH_GLOBALLABEL*>( pair.first );
if( gLabel )
{
int layer = pair.second;
SCH_FIELD* intersheetRef = gLabel->GetIntersheetRefs();
children.emplace_back( KIGFX::VIEW::LAYER_ITEM_PAIR( intersheetRef, layer ) );
} }
} }
selectedItems.insert( selectedItems.end(), children.begin(), children.end() ); EDA_RECT selectionRect( (wxPoint) area.GetOrigin(), wxSize( width, height ) );
selectionRect.Normalize();
int height = area.GetEnd().y - area.GetOrigin().y;
bool anyAdded = false; bool anyAdded = false;
bool anySubtracted = false; bool anySubtracted = false;
auto selectItem =
[&]( SCH_ITEM* aItem )
{
if( m_subtractive || ( m_exclusive_or && aItem->IsSelected() ) )
{
unselect( aItem );
anySubtracted = true;
}
else
{
select( aItem );
aItem->SetFlags( STARTPOINT | ENDPOINT );
anyAdded = true;
}
};
// Construct an EDA_RECT to determine EDA_ITEM selection for( SCH_ITEM* item : nearbyItems )
EDA_RECT selectionRect( (wxPoint) area.GetOrigin(), wxSize( width, height ) );
selectionRect.Normalize();
for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
{ {
EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pair.first ); if( Selectable( item ) && item->HitTest( selectionRect, isWindowSelection ) )
if( item && Selectable( item ) && item->HitTest( selectionRect, windowSelection ) )
{ {
if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) ) item->SetFlags( TEMP_SELECTED );
{ selectItem( item );
unselect( item ); }
anySubtracted = true; }
}
else for( SCH_ITEM* item : nearbyChildren )
{ {
select( item ); if( Selectable( item )
item->SetFlags( STARTPOINT | ENDPOINT ); && !item->GetParent()->HasFlag( TEMP_SELECTED )
anyAdded = true; && item->HitTest( selectionRect, isWindowSelection ) )
} {
selectItem( item );
} }
} }