Add system for property change notifications

Use this to sync symbol field edits that are
synced by the dialog

Fixes https://gitlab.com/kicad/code/kicad/-/issues/15697
This commit is contained in:
Jon Evans 2023-11-28 21:57:41 -05:00
parent a817eaa67f
commit 9c1a160fcd
5 changed files with 158 additions and 4 deletions

View File

@ -19,6 +19,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <inspectable.h>
#include <properties/property_mgr.h>
#include <properties/property.h>
@ -427,3 +428,33 @@ PROPERTY_MANAGER::CLASSES_INFO PROPERTY_MANAGER::GetAllClasses()
return rv;
}
void PROPERTY_MANAGER::PropertyChanged( INSPECTABLE* aObject, PROPERTY_BASE* aProperty )
{
auto listeners = m_listeners.find( TYPE_HASH( *aObject ) );
if( listeners == m_listeners.end() )
return;
for( const PROPERTY_LISTENER& listener : listeners->second )
listener( aObject, aProperty, m_managedCommit );
}
PROPERTY_COMMIT_HANDLER::PROPERTY_COMMIT_HANDLER( COMMIT* aCommit )
{
wxCHECK2_MSG( PROPERTY_MANAGER::Instance().m_managedCommit == nullptr,
return, "Can't have more than one managed commit at a time!" );
PROPERTY_MANAGER::Instance().m_managedCommit = aCommit;
}
PROPERTY_COMMIT_HANDLER::~PROPERTY_COMMIT_HANDLER()
{
wxASSERT_MSG( PROPERTY_MANAGER::Instance().m_managedCommit != nullptr,
"Something went wrong: m_managedCommit already null!" );
PROPERTY_MANAGER::Instance().m_managedCommit = nullptr;
}

View File

@ -18,9 +18,11 @@
*/
#include <bus_alias.h>
#include <commit.h>
#include <connection_graph.h>
#include <core/ignore.h>
#include <core/kicad_algo.h>
#include <ee_collectors.h>
#include <erc_settings.h>
#include <sch_marker.h>
#include <project.h>
@ -44,6 +46,75 @@ SCHEMATIC::SCHEMATIC( PROJECT* aPrj ) :
m_connectionGraph = new CONNECTION_GRAPH( this );
SetProject( aPrj );
PROPERTY_MANAGER::Instance().RegisterListener( TYPE_HASH( SCH_FIELD ),
[&]( INSPECTABLE* aItem, PROPERTY_BASE* aProperty, COMMIT* aCommit )
{
// Special case: propagate value, footprint, and datasheet fields to other units
// of a given symbol if they aren't in the selection
SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( aItem );
if( !field || !IsValid() )
return;
SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( field->GetParent() );
if( !symbol || aProperty->Name() != _HKI( "Text" ) )
return;
// TODO(JE) This will need to get smarter to enable API access
SCH_SHEET_PATH sheetPath = CurrentSheet();
wxString newValue = aItem->Get<wxString>( aProperty );
wxString ref = symbol->GetRef( &sheetPath );
int unit = symbol->GetUnit();
LIB_ID libId = symbol->GetLibId();
for( SCH_SHEET_PATH& sheet : GetSheets() )
{
std::vector<SCH_SYMBOL*> otherUnits;
CollectOtherUnits( ref, unit, libId, sheet, &otherUnits );
for( SCH_SYMBOL* otherUnit : otherUnits )
{
switch( field->GetId() )
{
case VALUE_FIELD:
{
if( aCommit )
aCommit->Modify( otherUnit, sheet.LastScreen() );
otherUnit->SetValueFieldText( newValue );
break;
}
case FOOTPRINT_FIELD:
{
if( aCommit )
aCommit->Modify( otherUnit, sheet.LastScreen() );
otherUnit->SetFootprintFieldText( newValue );
break;
}
case DATASHEET_FIELD:
{
if( aCommit )
aCommit->Modify( otherUnit, sheet.LastScreen() );
otherUnit->GetField( DATASHEET_FIELD )->SetText( newValue );
break;
}
default:
break;
}
}
}
} );
}

View File

@ -192,6 +192,8 @@ void SCH_PROPERTIES_PANEL::valueChanged( wxPropertyGridEvent& aEvent )
SCH_COMMIT changes( m_frame );
SCH_SCREEN* screen = m_frame->GetScreen();
PROPERTY_COMMIT_HANDLER handler( &changes );
for( EDA_ITEM* edaItem : selection )
{
SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );

View File

@ -39,31 +39,41 @@ public:
{
}
bool Set( PROPERTY_BASE* aProperty, wxAny& aValue )
bool Set( PROPERTY_BASE* aProperty, wxAny& aValue, bool aNotify = true )
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
void* object = propMgr.TypeCast( this, TYPE_HASH( *this ), aProperty->OwnerHash() );
if( object )
{
aProperty->setter( object, aValue );
if( aNotify )
propMgr.PropertyChanged( this, aProperty );
}
return object != nullptr;
}
template<typename T>
bool Set( PROPERTY_BASE* aProperty, T aValue )
bool Set( PROPERTY_BASE* aProperty, T aValue, bool aNotify = true )
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
void* object = propMgr.TypeCast( this, TYPE_HASH( *this ), aProperty->OwnerHash() );
if( object )
{
aProperty->set<T>( object, aValue );
if( aNotify )
propMgr.PropertyChanged( this, aProperty );
}
return object != nullptr;
}
template<typename T>
bool Set( const wxString& aProperty, T aValue )
bool Set( const wxString& aProperty, T aValue, bool aNotify = true )
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
TYPE_ID thisType = TYPE_HASH( *this );
@ -75,7 +85,12 @@ public:
object = propMgr.TypeCast( this, thisType, prop->OwnerHash() );
if( object )
{
prop->set<T>( object, aValue );
if( aNotify )
propMgr.PropertyChanged( this, prop );
}
}
return object != nullptr;

View File

@ -41,6 +41,7 @@ class PROPERTY_BASE;
class TYPE_CAST_BASE;
class ORIGIN_TRANSFORMS;
class INSPECTABLE;
class COMMIT;
///< Unique type identifier
using TYPE_ID = size_t;
@ -56,6 +57,16 @@ using PROPERTY_FUNCTOR_MAP = PROPERTY_MAP<std::function<bool( INSPECTABLE* )>>;
using PROPERTY_DISPLAY_ORDER = std::map<PROPERTY_BASE*, int>;
using PROPERTY_LISTENER = std::function<void( INSPECTABLE*, PROPERTY_BASE*, COMMIT* )>;
class PROPERTY_COMMIT_HANDLER
{
public:
PROPERTY_COMMIT_HANDLER( COMMIT* aCommit );
~PROPERTY_COMMIT_HANDLER();
};
/**
* Provide class metadata. Each class handled by PROPERTY_MANAGER
* needs to be described using AddProperty(), AddTypeCast() and InheritsAfter() methods.
@ -249,12 +260,32 @@ public:
std::vector<TYPE_ID> GetMatchingClasses( PROPERTY_BASE* aProperty );
/**
* Callback to alert the notification system that a property has changed
* @param aObject is the object whose property just changed
* @param aProperty is the property that changed
*/
void PropertyChanged( INSPECTABLE* aObject, PROPERTY_BASE* aProperty );
/**
* Registers a listener for the given type
* @param aType is the type to add the listener for
* @param aListenerFunc will be called every time a property on aType is changed
*/
void RegisterListener( TYPE_ID aType, PROPERTY_LISTENER aListenerFunc )
{
m_listeners[aType].emplace_back( aListenerFunc );
}
private:
PROPERTY_MANAGER() :
m_dirty( false )
m_dirty( false ),
m_managedCommit( nullptr )
{
}
friend class PROPERTY_COMMIT_HANDLER;
///< Structure holding type meta-data
struct CLASS_DESC
{
@ -324,6 +355,10 @@ private:
/// Flag indicating that the list of properties needs to be rebuild (RebuildProperties())
bool m_dirty;
std::map<TYPE_ID, std::vector<PROPERTY_LISTENER>> m_listeners;
COMMIT* m_managedCommit;
};