Separate selection change updates from properties updates

Fixes https://gitlab.com/kicad/code/kicad/-/issues/13624
This commit is contained in:
Jon Evans 2023-01-22 11:32:38 -05:00
parent 2628ab3e16
commit 351f668645
4 changed files with 107 additions and 36 deletions

View File

@ -129,10 +129,13 @@ void PROPERTIES_PANEL::OnLanguageChanged()
} }
void PROPERTIES_PANEL::update( const SELECTION& aSelection ) void PROPERTIES_PANEL::rebuildProperties( const SELECTION& aSelection )
{ {
if( *m_cachedSelection == aSelection ) if( *m_cachedSelection == aSelection )
{
updatePropertyValues( aSelection );
return; return;
}
*m_cachedSelection = aSelection; *m_cachedSelection = aSelection;
@ -223,47 +226,34 @@ void PROPERTIES_PANEL::update( const SELECTION& aSelection )
// Either determine the common value for a property or "<...>" to indicate multiple values // Either determine the common value for a property or "<...>" to indicate multiple values
bool available = true; bool available = true;
wxVariant commonVal, itemVal; bool writeable = true;
wxVariant commonVal;
bool writeable = property->Writeable( aSelection.Front() );
for( EDA_ITEM* item : aSelection ) for( EDA_ITEM* item : aSelection )
{ {
if( !propMgr.IsAvailableFor( TYPE_HASH( *item ), property, item ) ) if( !propMgr.IsAvailableFor( TYPE_HASH( *item ), property, item ) )
{
available = false;
break; // there is an item that does not have this property, so do not display it break; // there is an item that does not have this property, so do not display it
}
// If read-only for any of the selection, read-only for the whole selection. // If read-only for any of the selection, read-only for the whole selection.
if( !property->Writeable( item ) ) if( !property->Writeable( item ) )
writeable = false; writeable = false;
wxVariant& value = commonVal.IsNull() ? commonVal : itemVal; wxVariant value = commonVal;
const wxAny& any = item->Get( property );
bool converted = false;
if( property->HasChoices() ) if( getItemValue( item, property, value ) )
{ {
// handle enums as ints, since there are no default conversion functions for wxAny // Null value indicates different property values between items
int tmp; if( !commonVal.IsNull() && value != commonVal )
converted = any.GetAs<int>( &tmp ); commonVal.MakeNull();
else
if( converted ) commonVal = value;
value = wxVariant( tmp );
} }
else
if( !converted ) // all other types
converted = any.GetAs( &value );
if( !converted )
{ {
wxFAIL_MSG( wxS( "Could not convert wxAny to wxVariant" ) );
available = false; available = false;
break;
}
if( !commonVal.IsNull() && value != commonVal )
{
commonVal.MakeNull(); // items have different values for this property
break;
} }
} }
@ -313,6 +303,31 @@ void PROPERTIES_PANEL::update( const SELECTION& aSelection )
} }
bool PROPERTIES_PANEL::getItemValue( EDA_ITEM* aItem, PROPERTY_BASE* aProperty, wxVariant& aValue )
{
const wxAny& any = aItem->Get( aProperty );
bool converted = false;
if( aProperty->HasChoices() )
{
// handle enums as ints, since there are no default conversion functions for wxAny
int tmp;
converted = any.GetAs<int>( &tmp );
if( converted )
aValue = wxVariant( tmp );
}
if( !converted ) // all other types
converted = any.GetAs( &aValue );
if( !converted )
wxFAIL_MSG( wxS( "Could not convert wxAny to wxVariant" ) );
return converted;
}
void PROPERTIES_PANEL::onShow( wxShowEvent& aEvent ) void PROPERTIES_PANEL::onShow( wxShowEvent& aEvent )
{ {
if( aEvent.IsShown() ) if( aEvent.IsShown() )

View File

@ -31,6 +31,7 @@
#include <memory> #include <memory>
class EDA_BASE_FRAME; class EDA_BASE_FRAME;
class EDA_ITEM;
class SELECTION; class SELECTION;
class PROPERTY_BASE; class PROPERTY_BASE;
class wxStaticText; class wxStaticText;
@ -71,7 +72,27 @@ public:
void OnLanguageChanged(); void OnLanguageChanged();
protected: protected:
virtual void update( const SELECTION& aSelection ); /**
* Generates the property grid for a given selection of items.
*
* Calling this will remove any existing properties shown, causing visible flicker on some
* platforms and canceling any pending edits. Make sure to only call this when the group of
* selected items has actually changed.
*
* @param aSelection is a set of items to show properties for.
*/
virtual void rebuildProperties( const SELECTION& aSelection );
/**
* Updates the values shown in the property grid for the current selection.
*
* Does not add or remove properties from the display (@see rebuildProperties)
* This implies that aSelection must have the same contents as m_cachedSelection.
*
* @param aSelection is the set of selected items.
*/
virtual void updatePropertyValues( const SELECTION& aSelection ) {}
virtual wxPGProperty* createPGProperty( const PROPERTY_BASE* aProperty ) const = 0; virtual wxPGProperty* createPGProperty( const PROPERTY_BASE* aProperty ) const = 0;
// Event handlers // Event handlers
@ -80,6 +101,13 @@ protected:
void onCharHook( wxKeyEvent& aEvent ); void onCharHook( wxKeyEvent& aEvent );
void onShow( wxShowEvent& aEvent ); void onShow( wxShowEvent& aEvent );
/**
* Utility to fetch a property value and convert to wxVariant
* Precondition: aItem is known to have property aProperty
* @return true if conversion succeeded
*/
bool getItemValue( EDA_ITEM* aItem, PROPERTY_BASE* aProperty, wxVariant& aValue );
protected: protected:
std::vector<PROPERTY_BASE*> m_displayed; std::vector<PROPERTY_BASE*> m_displayed;
wxPropertyGrid* m_grid; wxPropertyGrid* m_grid;
@ -89,6 +117,7 @@ protected:
/// Proportion of the grid column splitter that is used for the key column (0.0 - 1.0) /// Proportion of the grid column splitter that is used for the key column (0.0 - 1.0)
float m_splitter_key_proportion; float m_splitter_key_proportion;
/// A copy of the most recent selection passed to rebuildProperties
std::unique_ptr<SELECTION> m_cachedSelection; std::unique_ptr<SELECTION> m_cachedSelection;
}; };

View File

@ -77,7 +77,9 @@ void PCB_PROPERTIES_PANEL::UpdateData()
// TODO perhaps it could be called less often? use PROPERTIES_TOOL and catch MODEL_RELOAD? // TODO perhaps it could be called less often? use PROPERTIES_TOOL and catch MODEL_RELOAD?
updateLists( static_cast<PCB_EDIT_FRAME*>( m_frame )->GetBoard() ); updateLists( static_cast<PCB_EDIT_FRAME*>( m_frame )->GetBoard() );
update( selection );
// Will actually just be updatePropertyValues() if selection hasn't changed
rebuildProperties( selection );
} }
@ -85,7 +87,20 @@ void PCB_PROPERTIES_PANEL::AfterCommit()
{ {
PCB_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<PCB_SELECTION_TOOL>(); PCB_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<PCB_SELECTION_TOOL>();
const SELECTION& selection = selectionTool->GetSelection(); const SELECTION& selection = selectionTool->GetSelection();
BOARD_ITEM* firstItem = static_cast<BOARD_ITEM*>( selection.Front() );
updatePropertyValues( selection );
CallAfter( [&]()
{
static_cast<PCB_EDIT_FRAME*>( m_frame )->GetCanvas()->SetFocus();
} );
}
void PCB_PROPERTIES_PANEL::updatePropertyValues( const SELECTION& aSelection )
{
// TODO: Refactor to reduce duplication with PROPERTIES_PANEL::rebuildProperties
BOARD_ITEM* firstItem = static_cast<BOARD_ITEM*>( aSelection.Front() );
for( wxPropertyGridIterator it = m_grid->GetIterator(); !it.AtEnd(); it.Next() ) for( wxPropertyGridIterator it = m_grid->GetIterator(); !it.AtEnd(); it.Next() )
{ {
@ -96,17 +111,27 @@ void PCB_PROPERTIES_PANEL::AfterCommit()
wxCHECK2( property, continue ); wxCHECK2( property, continue );
bool writeable = true; bool writeable = true;
wxVariant commonVal;
for( EDA_ITEM* edaItem : selection ) for( EDA_ITEM* edaItem : aSelection )
{
writeable &= property->Writeable( edaItem ); writeable &= property->Writeable( edaItem );
wxVariant value = commonVal;
if( getItemValue( edaItem, property, value ) )
{
// Null value indicates different property values between items
if( !commonVal.IsNull() && value != commonVal )
commonVal.MakeNull();
else
commonVal = value;
}
}
pgProp->SetValue( commonVal );
pgProp->Enable( writeable ); pgProp->Enable( writeable );
} }
CallAfter( [&]()
{
static_cast<PCB_EDIT_FRAME*>( m_frame )->GetCanvas()->SetFocus();
} );
} }

View File

@ -48,6 +48,8 @@ protected:
///> Regenerates caches storing layer and net names ///> Regenerates caches storing layer and net names
void updateLists( const BOARD* aBoard ); void updateLists( const BOARD* aBoard );
void updatePropertyValues( const SELECTION& aSelection ) override;
PCB_EDIT_FRAME* m_frame; PCB_EDIT_FRAME* m_frame;
PROPERTY_MANAGER& m_propMgr; PROPERTY_MANAGER& m_propMgr;
PG_UNIT_EDITOR* m_editor; PG_UNIT_EDITOR* m_editor;