Properties: Allow dynamic update of read-only state

This commit is contained in:
Jon Evans 2022-12-08 22:24:13 -05:00
parent b85fab9ab6
commit fbaf4af489
10 changed files with 146 additions and 18 deletions

View File

@ -73,7 +73,7 @@ wxPGWindowList PG_UNIT_EDITOR::CreateControls( wxPropertyGrid* aPropGrid, wxPGPr
if( PGPROPERTY_DISTANCE* prop = dynamic_cast<PGPROPERTY_DISTANCE*>( aProperty ) )
m_unitBinder->SetCoordType( prop->CoordType() );
else if( dynamic_cast<PGPROPERTY_ANGLE*>( aProperty ) )
else if( dynamic_cast<PGPROPERTY_ANGLE*>( aProperty ) != nullptr )
m_unitBinder->SetUnits( EDA_UNITS::DEGREES );
UpdateControl( aProperty, win );
@ -94,6 +94,11 @@ void PG_UNIT_EDITOR::UpdateControl( wxPGProperty* aProperty, wxWindow* aCtrl ) c
{
m_unitBinder->ChangeValue( var.GetDouble() );
}
else if( var.GetType() == wxT( "EDA_ANGLE" ) )
{
EDA_ANGLE_VARIANT_DATA* angleData = static_cast<EDA_ANGLE_VARIANT_DATA*>( var.GetData() );
m_unitBinder->ChangeAngleValue( angleData->Angle() );
}
else if( !aProperty->IsValueUnspecified() )
{
wxFAIL_MSG( wxT( "PG_UNIT_EDITOR should only be used with numeric properties!" ) );
@ -135,17 +140,32 @@ bool PG_UNIT_EDITOR::GetValueFromControl( wxVariant& aVariant, wxPGProperty* aPr
aVariant.MakeNull();
return true;
}
bool changed = false;
bool changed;
if( dynamic_cast<PGPROPERTY_ANGLE*>( aProperty ) )
if( dynamic_cast<PGPROPERTY_ANGLE*>( aProperty ) != nullptr )
{
double result = m_unitBinder->GetAngleValue().AsDegrees();
changed = ( aVariant.IsNull() || result != aVariant.GetDouble() );
EDA_ANGLE angle = m_unitBinder->GetAngleValue();
if( changed )
if( aVariant.GetType() == wxT( "EDA_ANGLE" ) )
{
aVariant = result;
m_unitBinder->SetValue( result );
EDA_ANGLE_VARIANT_DATA* ad = static_cast<EDA_ANGLE_VARIANT_DATA*>( aVariant.GetData() );
changed = ( aVariant.IsNull() || angle != ad->Angle() );
if( changed )
{
ad->SetAngle( angle );
m_unitBinder->SetAngleValue( angle );
}
}
else
{
changed = ( aVariant.IsNull() || angle.AsDegrees() != aVariant.GetDouble() );
if( changed )
{
aVariant = angle.AsDegrees();
m_unitBinder->SetValue( angle.AsDegrees() );
}
}
}
else

View File

@ -161,7 +161,6 @@ wxPGProperty* PGPropertyFactory( const PROPERTY_BASE* aProperty )
{
ret->SetLabel( wxGetTranslation( aProperty->Name() ) );
ret->SetName( aProperty->Name() );
ret->Enable( !aProperty->IsReadOnly() );
ret->SetHelpString( wxGetTranslation( aProperty->Name() ) );
ret->SetClientData( const_cast<PROPERTY_BASE*>( aProperty ) );
}

View File

@ -225,11 +225,17 @@ void PROPERTIES_PANEL::update( const SELECTION& aSelection )
bool available = true;
wxVariant commonVal, itemVal;
bool writeable = property->Writeable( aSelection.Front() );
for( EDA_ITEM* item : aSelection )
{
if( !propMgr.IsAvailableFor( TYPE_HASH( *item ), property, item ) )
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( !property->Writeable( item ) )
writeable = false;
wxVariant& value = commonVal.IsNull() ? commonVal : itemVal;
const wxAny& any = item->Get( property );
bool converted = false;
@ -268,6 +274,7 @@ void PROPERTIES_PANEL::update( const SELECTION& aSelection )
if( pgProp )
{
pgProp->SetValue( commonVal );
pgProp->Enable( writeable );
m_displayed.push_back( property );
wxASSERT( displayOrder.count( property ) );

View File

@ -45,6 +45,8 @@ public:
virtual void UpdateData() = 0;
virtual void AfterCommit() {}
wxPropertyGrid* GetPropertyGrid()
{
return m_grid;

View File

@ -113,6 +113,9 @@ public:
wxValidator* DoGetValidator() const override;
///> Do not perform PG validation; the UX is not what we want.
bool ValidateValue( wxVariant&, wxPGValidationInfo& ) const override { return true; }
protected:
///> Scale factor to convert between raw and displayed value
double m_scale;

View File

@ -26,6 +26,7 @@
#include <core/wx_stl_compat.h>
#include <origin_transforms.h>
#include <properties/eda_angle_variant.h>
#include <wx/any.h>
#include <wx/string.h>
@ -184,7 +185,8 @@ PROPERTY_BASE( const wxString& aName, PROPERTY_DISPLAY aDisplay = PT_DEFAULT,
m_display( aDisplay ),
m_coordType( aCoordType ),
m_isInternal( false ),
m_availFunc( [](INSPECTABLE*)->bool { return true; } )
m_availFunc( [](INSPECTABLE*)->bool { return true; } ),
m_writeableFunc( [](INSPECTABLE*)->bool { return true; } )
{
}
@ -240,6 +242,16 @@ PROPERTY_BASE( const wxString& aName, PROPERTY_DISPLAY aDisplay = PT_DEFAULT,
m_availFunc = aFunc;
}
virtual bool Writeable( INSPECTABLE* aObject ) const
{
return m_writeableFunc( aObject );
}
void SetWriteableFunc( std::function<bool(INSPECTABLE*)> aFunc )
{
m_writeableFunc = aFunc;
}
/**
* Return type-id of the Owner class.
*/
@ -255,8 +267,6 @@ PROPERTY_BASE( const wxString& aName, PROPERTY_DISPLAY aDisplay = PT_DEFAULT,
*/
virtual size_t TypeHash() const = 0;
virtual bool IsReadOnly() const = 0;
PROPERTY_DISPLAY Display() const
{
return m_display;
@ -281,10 +291,18 @@ protected:
// we used a UInt editor.
if( std::is_same<T, wxVariant>::value )
{
wxVariant var = static_cast<wxVariant>( aValue );
wxAny pv = getter( aObject );
if( pv.CheckType<unsigned>() )
a = static_cast<unsigned>( static_cast<wxVariant>( aValue ).GetLong() );
{
a = static_cast<unsigned>( var.GetLong() );
}
else if( pv.CheckType<EDA_ANGLE>() )
{
EDA_ANGLE_VARIANT_DATA* ad = static_cast<EDA_ANGLE_VARIANT_DATA*>( var.GetData() );
a = ad->Angle();
}
}
setter( aObject, a );
@ -318,6 +336,8 @@ private:
std::function<bool(INSPECTABLE*)> m_availFunc; ///< Eval to determine if prop is available
std::function<bool(INSPECTABLE*)> m_writeableFunc; ///< Eval to determine if prop is read-only
friend class INSPECTABLE;
};
@ -363,9 +383,9 @@ public:
return m_typeHash;
}
bool IsReadOnly() const override
bool Writeable( INSPECTABLE* aObject ) const override
{
return !m_setter;
return m_setter && PROPERTY_BASE::Writeable( aObject );
}
protected:
@ -381,7 +401,7 @@ protected:
virtual void setter( void* obj, wxAny& v ) override
{
wxCHECK( !IsReadOnly(), /*void*/ );
wxCHECK( m_setter, /*void*/ );
if( !v.CheckType<T>() )
throw std::invalid_argument( "Invalid type requested" );
@ -450,7 +470,7 @@ public:
void setter( void* obj, wxAny& v ) override
{
wxCHECK( !( PROPERTY<Owner, T, Base>::IsReadOnly() ), /*void*/ );
wxCHECK( ( PROPERTY<Owner, T, Base>::m_setter ), /*void*/ );
Owner* o = reinterpret_cast<Owner*>( obj );
if( v.CheckType<T>() )

View File

@ -81,6 +81,30 @@ void PCB_PROPERTIES_PANEL::UpdateData()
}
void PCB_PROPERTIES_PANEL::AfterCommit()
{
PCB_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<PCB_SELECTION_TOOL>();
const SELECTION& selection = selectionTool->GetSelection();
BOARD_ITEM* firstItem = static_cast<BOARD_ITEM*>( selection.Front() );
for( wxPropertyGridIterator it = m_grid->GetIterator(); !it.AtEnd(); it.Next() )
{
wxPGProperty* pgProp = it.GetProperty();
PROPERTY_BASE* property = m_propMgr.GetProperty( TYPE_HASH( *firstItem ),
pgProp->GetName() );
wxASSERT( property );
bool writeable = true;
for( EDA_ITEM* edaItem : selection )
writeable &= property->Writeable( edaItem );
pgProp->Enable( writeable );
}
}
wxPGProperty* PCB_PROPERTIES_PANEL::createPGProperty( const PROPERTY_BASE* aProperty ) const
{
if( aProperty->TypeHash() == TYPE_HASH( PCB_LAYER_ID ) )
@ -100,7 +124,6 @@ wxPGProperty* PCB_PROPERTIES_PANEL::createPGProperty( const PROPERTY_BASE* aProp
ret->SetLabel( wxGetTranslation( aProperty->Name() ) );
ret->SetName( aProperty->Name() );
ret->Enable( !aProperty->IsReadOnly() );
ret->SetHelpString( wxGetTranslation( aProperty->Name() ) );
ret->SetClientData( const_cast<PROPERTY_BASE*>( aProperty ) );
@ -133,6 +156,9 @@ void PCB_PROPERTIES_PANEL::valueChanged( wxPropertyGridEvent& aEvent )
changes.Push( _( "Change property" ) );
m_frame->Refresh();
// Perform grid updates as necessary based on value change
AfterCommit();
}

View File

@ -38,6 +38,8 @@ public:
void UpdateData() override;
void AfterCommit() override;
protected:
wxPGProperty* createPGProperty( const PROPERTY_BASE* aProperty ) const override;

View File

@ -1366,6 +1366,15 @@ static struct ZONE_DESC
.Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) );
}
ENUM_MAP<ZONE_FILL_MODE>& zfmMap = ENUM_MAP<ZONE_FILL_MODE>::Instance();
if( zfmMap.Choices().GetCount() == 0 )
{
zfmMap.Undefined( ZONE_FILL_MODE::POLYGONS );
zfmMap.Map( ZONE_FILL_MODE::POLYGONS, _HKI( "Solid fill" ) )
.Map( ZONE_FILL_MODE::HATCH_PATTERN, _HKI( "Hatch pattern" ) );
}
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( ZONE );
propMgr.InheritsAfter( TYPE_HASH( ZONE ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
@ -1395,6 +1404,15 @@ static struct ZONE_DESC
return false;
};
auto isHatchedFill =
[]( INSPECTABLE* aItem ) -> bool
{
if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
return zone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN;
return false;
};
auto layer = new PROPERTY_ENUM<ZONE, PCB_LAYER_ID>( _HKI( "Layer" ),
&ZONE::SetLayer, &ZONE::GetLayer );
@ -1414,6 +1432,35 @@ static struct ZONE_DESC
propMgr.AddProperty( new PROPERTY<ZONE, wxString>( _HKI( "Name" ),
&ZONE::SetZoneName, &ZONE::GetZoneName ) );
const wxString groupFill = _( "Fill Style" );
propMgr.AddProperty( new PROPERTY_ENUM<ZONE, ZONE_FILL_MODE>( _HKI( "Fill Mode" ),
&ZONE::SetFillMode, &ZONE::GetFillMode ), groupFill );
auto hatchOrientation = new PROPERTY<ZONE, EDA_ANGLE>( _HKI( "Orientation" ),
&ZONE::SetHatchOrientation, &ZONE::GetHatchOrientation,
PROPERTY_DISPLAY::PT_DEGREE );
hatchOrientation->SetWriteableFunc( isHatchedFill );
propMgr.AddProperty( hatchOrientation, groupFill );
//TODO: Switch to translated
auto hatchWidth = new PROPERTY<ZONE, int>( wxT( "Hatch Width" ),
&ZONE::SetHatchThickness, &ZONE::GetHatchThickness,
PROPERTY_DISPLAY::PT_SIZE );
hatchWidth->SetWriteableFunc( isHatchedFill );
propMgr.AddProperty( hatchWidth, groupFill );
//TODO: Switch to translated
auto hatchGap = new PROPERTY<ZONE, int>( wxT( "Hatch Gap" ),
&ZONE::SetHatchGap, &ZONE::GetHatchGap,
PROPERTY_DISPLAY::PT_SIZE );
hatchGap->SetWriteableFunc( isHatchedFill );
propMgr.AddProperty( hatchGap, groupFill );
// TODO: Smoothing effort needs to change to enum (in dialog too)
// TODO: Smoothing amount (double)
// Unexposed properties (HatchHoleMinArea / HatchBorderAlgorithm)?
const wxString groupOverrides = _( "Overrides" );
auto clearanceOverride = new PROPERTY<ZONE, int>( _HKI( "Clearance Override" ),
@ -1449,3 +1496,4 @@ static struct ZONE_DESC
} _ZONE_DESC;
IMPLEMENT_ENUM_TO_WXANY( ZONE_CONNECTION )
IMPLEMENT_ENUM_TO_WXANY( ZONE_FILL_MODE )

View File

@ -920,6 +920,7 @@ public:
#ifndef SWIG
DECLARE_ENUM_TO_WXANY( ZONE_CONNECTION )
DECLARE_ENUM_TO_WXANY( ZONE_FILL_MODE )
#endif
#endif // ZONE_H