diff --git a/common/properties/pg_editors.cpp b/common/properties/pg_editors.cpp index b423f80b06..acc15f6d20 100644 --- a/common/properties/pg_editors.cpp +++ b/common/properties/pg_editors.cpp @@ -73,7 +73,7 @@ wxPGWindowList PG_UNIT_EDITOR::CreateControls( wxPropertyGrid* aPropGrid, wxPGPr if( PGPROPERTY_DISTANCE* prop = dynamic_cast( aProperty ) ) m_unitBinder->SetCoordType( prop->CoordType() ); - else if( dynamic_cast( aProperty ) ) + else if( dynamic_cast( 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( 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( aProperty ) ) + if( dynamic_cast( 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( 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 diff --git a/common/properties/pg_properties.cpp b/common/properties/pg_properties.cpp index ad6f11457c..c9735d926d 100644 --- a/common/properties/pg_properties.cpp +++ b/common/properties/pg_properties.cpp @@ -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( aProperty ) ); } diff --git a/common/widgets/properties_panel.cpp b/common/widgets/properties_panel.cpp index 123215b6fa..bc219cc3da 100644 --- a/common/widgets/properties_panel.cpp +++ b/common/widgets/properties_panel.cpp @@ -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 ) ); diff --git a/common/widgets/properties_panel.h b/common/widgets/properties_panel.h index 1a3e643a09..513c2182e4 100644 --- a/common/widgets/properties_panel.h +++ b/common/widgets/properties_panel.h @@ -45,6 +45,8 @@ public: virtual void UpdateData() = 0; + virtual void AfterCommit() {} + wxPropertyGrid* GetPropertyGrid() { return m_grid; diff --git a/include/properties/pg_properties.h b/include/properties/pg_properties.h index 9fc32d5e49..998adce135 100644 --- a/include/properties/pg_properties.h +++ b/include/properties/pg_properties.h @@ -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; diff --git a/include/properties/property.h b/include/properties/property.h index 86eb27c505..f1e73a3442 100644 --- a/include/properties/property.h +++ b/include/properties/property.h @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -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 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::value ) { + wxVariant var = static_cast( aValue ); wxAny pv = getter( aObject ); if( pv.CheckType() ) - a = static_cast( static_cast( aValue ).GetLong() ); + { + a = static_cast( var.GetLong() ); + } + else if( pv.CheckType() ) + { + EDA_ANGLE_VARIANT_DATA* ad = static_cast( var.GetData() ); + a = ad->Angle(); + } } setter( aObject, a ); @@ -318,6 +336,8 @@ private: std::function m_availFunc; ///< Eval to determine if prop is available + std::function 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() ) throw std::invalid_argument( "Invalid type requested" ); @@ -450,7 +470,7 @@ public: void setter( void* obj, wxAny& v ) override { - wxCHECK( !( PROPERTY::IsReadOnly() ), /*void*/ ); + wxCHECK( ( PROPERTY::m_setter ), /*void*/ ); Owner* o = reinterpret_cast( obj ); if( v.CheckType() ) diff --git a/pcbnew/widgets/pcb_properties_panel.cpp b/pcbnew/widgets/pcb_properties_panel.cpp index feda0da147..f03dbddc44 100644 --- a/pcbnew/widgets/pcb_properties_panel.cpp +++ b/pcbnew/widgets/pcb_properties_panel.cpp @@ -81,6 +81,30 @@ void PCB_PROPERTIES_PANEL::UpdateData() } +void PCB_PROPERTIES_PANEL::AfterCommit() +{ + PCB_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool(); + const SELECTION& selection = selectionTool->GetSelection(); + BOARD_ITEM* firstItem = static_cast( 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( 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(); } diff --git a/pcbnew/widgets/pcb_properties_panel.h b/pcbnew/widgets/pcb_properties_panel.h index a5cf87ddb4..b4a3c07f37 100644 --- a/pcbnew/widgets/pcb_properties_panel.h +++ b/pcbnew/widgets/pcb_properties_panel.h @@ -38,6 +38,8 @@ public: void UpdateData() override; + void AfterCommit() override; + protected: wxPGProperty* createPGProperty( const PROPERTY_BASE* aProperty ) const override; diff --git a/pcbnew/zone.cpp b/pcbnew/zone.cpp index 0f9cd45aaa..11eea1fbc6 100644 --- a/pcbnew/zone.cpp +++ b/pcbnew/zone.cpp @@ -1366,6 +1366,15 @@ static struct ZONE_DESC .Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) ); } + ENUM_MAP& zfmMap = ENUM_MAP::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( aItem ) ) + return zone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN; + + return false; + }; auto layer = new PROPERTY_ENUM( _HKI( "Layer" ), &ZONE::SetLayer, &ZONE::GetLayer ); @@ -1414,6 +1432,35 @@ static struct ZONE_DESC propMgr.AddProperty( new PROPERTY( _HKI( "Name" ), &ZONE::SetZoneName, &ZONE::GetZoneName ) ); + const wxString groupFill = _( "Fill Style" ); + + propMgr.AddProperty( new PROPERTY_ENUM( _HKI( "Fill Mode" ), + &ZONE::SetFillMode, &ZONE::GetFillMode ), groupFill ); + + auto hatchOrientation = new PROPERTY( _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( 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( 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( _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 ) diff --git a/pcbnew/zone.h b/pcbnew/zone.h index ee7b04a329..1db627edf9 100644 --- a/pcbnew/zone.h +++ b/pcbnew/zone.h @@ -920,6 +920,7 @@ public: #ifndef SWIG DECLARE_ENUM_TO_WXANY( ZONE_CONNECTION ) +DECLARE_ENUM_TO_WXANY( ZONE_FILL_MODE ) #endif #endif // ZONE_H