Rename "Expand Selection" menu item in selection tool.

(We have a different "Expand Selection" command now,
and this was never a great name for showing
more choices anyway.)

Also treat tablecells as opaque when guessing
selection candidates.  It's unlikely there's anything
under them and keeping to their text bounds feels
counter-intuitive.
This commit is contained in:
Jeff Young
2025-10-06 21:15:34 +01:00
parent 38a52f2609
commit c12cc19ef4
8 changed files with 71 additions and 81 deletions
+8 -32
View File
@@ -58,7 +58,6 @@ EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ) :
m_rectangleHeight( 0 ),
m_rectangleWidth( 0 ),
m_cornerRadius( 0 ),
m_segmentLength( 0 ),
m_editState( 0 ),
m_proxyItem( false )
{
@@ -78,7 +77,6 @@ EDA_SHAPE::EDA_SHAPE( const SHAPE& aShape ) :
m_rectangleHeight( 0 ),
m_rectangleWidth( 0 ),
m_cornerRadius( 0 ),
m_segmentLength( 0 ),
m_editState( 0 ),
m_proxyItem( false )
{
@@ -463,20 +461,6 @@ void EDA_SHAPE::SetCornerRadius( int aRadius )
}
void EDA_SHAPE::SetLength( const double& aLength )
{
switch( m_shape )
{
case SHAPE_T::SEGMENT:
m_segmentLength = aLength;
break;
default:
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
}
}
void EDA_SHAPE::SetRectangleHeight( const int& aHeight )
{
switch ( m_shape )
@@ -522,20 +506,6 @@ void EDA_SHAPE::SetRectangle( const long long int& aHeight, const long long int&
}
void EDA_SHAPE::SetSegmentAngle( const EDA_ANGLE& aAngle )
{
switch( m_shape )
{
case SHAPE_T::SEGMENT:
m_segmentAngle = aAngle;
break;
default:
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
}
}
bool EDA_SHAPE::IsClosed() const
{
switch( m_shape )
@@ -1836,11 +1806,17 @@ void EDA_SHAPE::SetPolyPoints( const std::vector<VECTOR2I>& aPoints )
}
std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly ) const
std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly, bool aHittesting ) const
{
std::vector<SHAPE*> effectiveShapes;
int width = GetEffectiveWidth();
bool solidFill = ( IsSolidFill() || IsHatchedFill() || IsProxyItem() ) && !aEdgeOnly;
bool solidFill = IsSolidFill()
|| IsHatchedFill()
|| IsProxyItem()
|| ( aHittesting && IsFilledForHitTesting() );
if( aEdgeOnly )
solidFill = false;
switch( m_shape )
{
+15 -8
View File
@@ -261,18 +261,24 @@ bool SELECTION_TOOL::doSelectionMenu( COLLECTOR* aCollector )
EDA_ITEM* current = nullptr;
SELECTION highlightGroup;
bool selectAll = false;
bool expandSelection = false;
bool showMoreChoices = false;
highlightGroup.SetLayer( LAYER_SELECT_OVERLAY );
getView()->Add( &highlightGroup );
do
{
/// The user has requested the full, non-limited list of selection items
if( expandSelection )
/// This must be the second time through the loop, and the user has requested the full,
/// non-limited list of selection items
if( showMoreChoices )
{
aCollector->Combine();
expandSelection = false;
// prime event loop so we don't have to wait around for a mouse-moved
m_toolMgr->PrimeTool( { 0, 0 } );
showMoreChoices = false;
}
int limit = std::min( 100, aCollector->GetCount() );
ACTION_MENU menu( true );
@@ -306,8 +312,8 @@ bool SELECTION_TOOL::doSelectionMenu( COLLECTOR* aCollector )
menu.AppendSeparator();
menu.Add( _( "Select &All\tA" ), limit + 1, BITMAPS::INVALID_BITMAP );
if( !expandSelection && aCollector->HasAdditionalItems() )
menu.Add( _( "&Expand Selection\tE" ), limit + 2, BITMAPS::INVALID_BITMAP );
if( !showMoreChoices && aCollector->HasAdditionalItems() )
menu.Add( _( "Show &More Choices...\tM" ), limit + 2, BITMAPS::INVALID_BITMAP );
if( aCollector->m_MenuTitle.Length() )
{
@@ -387,7 +393,7 @@ bool SELECTION_TOOL::doSelectionMenu( COLLECTOR* aCollector )
{
selectAll = false;
current = nullptr;
expandSelection = true;
showMoreChoices = true;
}
// User has selected an item, so this one will be returned
else if( id && ( *id > 0 ) && ( *id <= limit ) )
@@ -410,7 +416,8 @@ bool SELECTION_TOOL::doSelectionMenu( COLLECTOR* aCollector )
getView()->UpdateItems();
getEditFrame<EDA_DRAW_FRAME>()->GetCanvas()->Refresh();
}
} while( expandSelection );
}
while( showMoreChoices );
getView()->Remove( &highlightGroup );
+5
View File
@@ -73,6 +73,11 @@ public:
int GetColumnWidth() const;
void SetColumnWidth( int aWidth );
bool IsFilledForHitTesting() const override
{
return true;
}
void Plot( PLOTTER* aPlotter, bool aBackground, const SCH_PLOT_OPTS& aPlotOpts,
int aUnit, int aBodyStyle, const VECTOR2I& aOffset, bool aDimmed ) override;
+3 -13
View File
@@ -1828,8 +1828,7 @@ void SCH_SELECTION_TOOL::GuessSelectionCandidates( SCH_COLLECTOR& collector, con
}
}
field->GetEffectiveTextShape( false, box, orient )
->Collide( poss, INT_MAX / 4, &dist );
field->GetEffectiveTextShape( false, box, orient )->Collide( poss, INT_MAX / 4, &dist );
}
else if( text )
{
@@ -1837,18 +1836,9 @@ void SCH_SELECTION_TOOL::GuessSelectionCandidates( SCH_COLLECTOR& collector, con
}
else if( shape )
{
std::vector<SHAPE*> shapes = shape->MakeEffectiveShapes();
auto shapes = std::make_shared<SHAPE_COMPOUND>( shape->MakeEffectiveShapesForHitTesting() );
for( SHAPE* s : shapes )
{
int shapeDist = dist;
s->Collide( poss, INT_MAX / 4, &shapeDist );
if( shapeDist < dist )
dist = shapeDist;
delete s;
}
shapes->Collide( poss, INT_MAX / 4, &dist );
// Filled shapes win hit tests anywhere inside them
dominating = shape->IsFilledForHitTesting();
+7 -8
View File
@@ -381,9 +381,12 @@ public:
return makeEffectiveShapes( aEdgeOnly );
}
void ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList );
virtual std::vector<SHAPE*> MakeEffectiveShapesForHitTesting() const
{
return makeEffectiveShapes( false, false, true );
}
void SetLength( const double& aLength );
void ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList );
void SetRectangleHeight( const int& aHeight );
@@ -394,8 +397,6 @@ public:
void SetCornerRadius( int aRadius );
int GetCornerRadius() const;
void SetSegmentAngle( const EDA_ANGLE& aAngle );
bool IsClockwiseArc() const;
/**
@@ -483,7 +484,8 @@ protected:
* lineChain rather than a closed polygon.
*/
// fixme: move to shape_compound
std::vector<SHAPE*> makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly = false ) const;
std::vector<SHAPE*> makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly = false,
bool aHittesting = false ) const;
virtual int getMaxError() const { return 100; }
@@ -501,9 +503,6 @@ protected:
long long int m_rectangleWidth;
int m_cornerRadius;
double m_segmentLength;
EDA_ANGLE m_segmentAngle;
VECTOR2I m_start; // Line start point or Circle center
VECTOR2I m_end; // Line end point or Circle 3 o'clock point
+14 -14
View File
@@ -393,7 +393,7 @@ INSPECT_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* aTestItem, void* aTestData
{
if( zone->HitTestForCorner( m_refPos, accuracy * 2 ) || zone->HitTestForEdge( m_refPos, accuracy ) )
{
Append( aTestItem );
Append( zone );
return INSPECT_RESULT::CONTINUE;
}
else if( !m_Guide->IgnoreZoneFills() )
@@ -402,7 +402,7 @@ INSPECT_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* aTestItem, void* aTestData
{
if( m_Guide->IsLayerVisible( layer ) && zone->HitTestFilledArea( layer, m_refPos ) )
{
Append( aTestItem );
Append( zone );
return INSPECT_RESULT::CONTINUE;
}
}
@@ -412,15 +412,15 @@ INSPECT_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* aTestItem, void* aTestData
{
if( footprint->HitTest( m_refPos, accuracy ) && footprint->HitTestAccurate( m_refPos, accuracy ) )
{
Append( aTestItem );
Append( footprint );
return INSPECT_RESULT::CONTINUE;
}
}
else if( pad || via )
{
if( aTestItem->HitTest( m_refPos, accuracy ) )
if( boardItem->HitTest( m_refPos, accuracy ) )
{
Append( aTestItem );
Append( boardItem );
return INSPECT_RESULT::CONTINUE;
}
}
@@ -437,9 +437,9 @@ INSPECT_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* aTestItem, void* aTestData
accuracy = KiROUND( accuracy * 1.5 );
}
if( aTestItem->HitTest( m_refPos, accuracy ) )
if( boardItem->HitTest( m_refPos, accuracy ) )
{
Append( aTestItem );
Append( boardItem );
return INSPECT_RESULT::CONTINUE;
}
}
@@ -464,7 +464,7 @@ INSPECT_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* aTestItem, void* aTestData
{
if( zone->HitTestForCorner( m_refPos, accuracy * 2 ) || zone->HitTestForEdge( m_refPos, accuracy ) )
{
Append2nd( aTestItem );
Append2nd( zone );
return INSPECT_RESULT::CONTINUE;
}
else if( !m_Guide->IgnoreZoneFills() )
@@ -473,7 +473,7 @@ INSPECT_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* aTestItem, void* aTestData
{
if( m_Guide->IsLayerVisible( layer ) && zone->HitTestFilledArea( layer, m_refPos ) )
{
Append2nd( aTestItem );
Append2nd( zone );
return INSPECT_RESULT::CONTINUE;
}
}
@@ -486,15 +486,15 @@ INSPECT_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* aTestItem, void* aTestData
if( footprint->HitTest( m_refPos, accuracy ) && footprint->HitTestAccurate( m_refPos, accuracy ) )
{
Append2nd( aTestItem );
Append2nd( footprint );
return INSPECT_RESULT::CONTINUE;
}
}
else if( pad || via )
{
if( aTestItem->HitTest( m_refPos, accuracy ) )
if( boardItem->HitTest( m_refPos, accuracy ) )
{
Append2nd( aTestItem );
Append2nd( boardItem );
return INSPECT_RESULT::CONTINUE;
}
}
@@ -507,9 +507,9 @@ INSPECT_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* aTestItem, void* aTestData
accuracy = KiROUND( accuracy * 1.5 );
}
if( aTestItem->HitTest( m_refPos, accuracy ) )
if( boardItem->HitTest( m_refPos, accuracy ) )
{
Append2nd( aTestItem );
Append2nd( boardItem );
return INSPECT_RESULT::CONTINUE;
}
}
+5
View File
@@ -77,6 +77,11 @@ public:
int GetColumnWidth() const;
void SetColumnWidth( int aWidth );
bool IsFilledForHitTesting() const override
{
return true;
}
wxString GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const override;
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
+14 -6
View File
@@ -3545,7 +3545,6 @@ int PCB_SELECTION_TOOL::hitTestDistance( const VECTOR2I& aWhere, BOARD_ITEM* aIt
}
case PCB_TEXTBOX_T:
case PCB_TABLECELL_T:
{
PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( aItem );
@@ -3556,17 +3555,26 @@ int PCB_SELECTION_TOOL::hitTestDistance( const VECTOR2I& aWhere, BOARD_ITEM* aIt
break;
}
case PCB_TABLECELL_T:
{
PCB_TABLECELL* tablecell = static_cast<PCB_TABLECELL*>( aItem );
auto shape = std::make_shared<SHAPE_COMPOUND>( tablecell->MakeEffectiveShapesForHitTesting() );
shape->Collide( loc, aMaxDistance, &distance );
break;
}
case PCB_TABLE_T:
{
PCB_TABLE* table = static_cast<PCB_TABLE*>( aItem );
distance = aMaxDistance;
for( PCB_TABLECELL* cell : table->GetCells() )
{
// Add a bit of slop to text-shapes
if( cell->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance ) )
distance = std::clamp( distance - ( aMaxDistance / 2 ), 0, distance );
}
distance = std::min( distance, hitTestDistance( aWhere, cell, aMaxDistance ) );
// Tables should defer to their table cells. Never consider them exact.
distance = std::clamp( distance + ( aMaxDistance / 4 ), 0, aMaxDistance );
break;
}