Files
ResInsight/Fwk/AppFwk/cafProjectDataModel/cafPdmCore/cafPdmDataValueField.h
T
Magne Sjaastad ab97381d94 Add ADL-based field traits, migrate all call sites, remove cafInternalPdmValueFieldSpecializations.h
Introduce cafPdmFieldTraits.h with ADL-based pdmToVariant/pdmFromVariant/pdmVariantEqual
as a replacement for PdmValueFieldSpecialization. Migrate all types (AppEnum, FilePath,
PdmPointer, cvf types) and all call sites (PdmDataValueField, PdmProxyValueField, UI
framework, ApplicationLibCode). Delete cafInternalPdmValueFieldSpecializations.h.
Add cafPdmFieldTraits unit tests and fix std::optional support.
2026-04-08 12:37:36 +02:00

342 lines
12 KiB
C++

//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2011-2013 Ceetron AS
//
// This library may be used under the terms of either the GNU General Public License or
// the GNU Lesser General Public License as follows:
//
// GNU General Public License Usage
// This library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <<http://www.gnu.org/licenses/gpl.html>>
// for more details.
//
// GNU Lesser General Public License Usage
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU Lesser General Public License at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
// for more details.
//
//##################################################################################################
#ifndef CAF_IS_DEFINING_PDM_FIELD
#pragma once
#endif
#include "cafPdmValueField.h"
#include "cafAssert.h"
#include "cafPdmFieldTraits.h"
#include "cafPdmUiFieldHandleInterface.h"
#include <QVariant>
#include <optional>
#include <type_traits>
#include <typeinfo>
#include <vector>
namespace caf
{
class PdmObjectHandle;
//==================================================================================================
/// Field class encapsulating data with input and output of this data to/from a QXmlStream
/// read/write-FieldData is supposed to be specialized for types needing specialization
//==================================================================================================
template <typename DataType>
class PdmDataValueField : public PdmValueField
{
public:
// Type traits magic to check if a template argument is a vector
template <typename T>
struct is_vector : public std::false_type
{
};
template <typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type
{
};
// Type trait to detect std::pair<bool, ArithmeticType>
template <typename T>
struct is_bool_arithmetic_pair : public std::false_type
{
};
template <typename T>
struct is_bool_arithmetic_pair<std::pair<bool, T>> : public std::integral_constant<bool, std::is_arithmetic<T>::value>
{
};
typedef DataType FieldDataType;
PdmDataValueField()
: m_fieldValue( DataType() )
, m_defaultFieldValue( DataType() )
{
}
PdmDataValueField( const PdmDataValueField& other ) { m_fieldValue = other.m_fieldValue; }
explicit PdmDataValueField( const DataType& fieldValue ) { m_fieldValue = fieldValue; }
~PdmDataValueField() override {}
// Assignment
PdmDataValueField& operator=( const PdmDataValueField& other )
{
CAF_ASSERT( isInitializedByInitFieldMacro() );
m_fieldValue = other.m_fieldValue;
return *this;
}
PdmDataValueField& operator=( const DataType& fieldValue )
{
CAF_ASSERT( isInitializedByInitFieldMacro() );
m_fieldValue = fieldValue;
return *this;
}
// Basic access
DataType value() const { return m_fieldValue; }
void setValue( const DataType& fieldValue )
{
CAF_ASSERT( isInitializedByInitFieldMacro() );
m_fieldValue = fieldValue;
}
void setValueWithFieldChanged( const DataType& fieldValue );
// Implementation of PdmValueField interface
QVariant toQVariant() const override
{
CAF_ASSERT( isInitializedByInitFieldMacro() );
using caf::pdmToVariant;
return pdmToVariant( m_fieldValue );
}
void setFromQVariant( const QVariant& variant ) override
{
CAF_ASSERT( isInitializedByInitFieldMacro() );
using caf::pdmFromVariant;
pdmFromVariant( variant, m_fieldValue );
}
bool isReadOnly() const override { return false; }
// Access operators
/*Conversion */ operator DataType() const { return m_fieldValue; }
const DataType& operator()() const { return m_fieldValue; }
DataType& v() { return m_fieldValue; } // This one breaches encapsulation. Remove ?
const DataType& v() const { return m_fieldValue; }
bool operator==( const DataType& fieldValue ) const { return m_fieldValue == fieldValue; }
bool operator!=( const DataType& fieldValue ) const { return m_fieldValue != fieldValue; }
// Range validation - only available for arithmetic types
template <typename T = DataType>
typename std::enable_if<std::is_arithmetic<T>::value>::type setRange( const DataType& minValue, const DataType& maxValue )
{
m_minValue = std::min( minValue, maxValue );
m_maxValue = std::max( minValue, maxValue );
}
template <typename T = DataType>
typename std::enable_if<std::is_arithmetic<T>::value>::type clearRange()
{
m_minValue.reset();
m_maxValue.reset();
}
template <typename T = DataType>
typename std::enable_if<std::is_arithmetic<T>::value>::type setMinValue( const DataType& minValue )
{
m_minValue = minValue;
}
template <typename T = DataType>
typename std::enable_if<std::is_arithmetic<T>::value>::type setMaxValue( const DataType& maxValue )
{
m_maxValue = maxValue;
}
template <typename T = DataType>
typename std::enable_if<std::is_arithmetic<T>::value>::type clearMinValue()
{
m_minValue.reset();
}
template <typename T = DataType>
typename std::enable_if<std::is_arithmetic<T>::value>::type clearMaxValue()
{
m_maxValue.reset();
}
// Range validation for std::pair<bool, ArithmeticType> - validates the second (value) element
template <typename T = DataType>
typename std::enable_if<is_bool_arithmetic_pair<T>::value>::type
setRange( const typename T::second_type& minValue, const typename T::second_type& maxValue )
{
m_minValue = DataType( false, std::min( minValue, maxValue ) );
m_maxValue = DataType( false, std::max( minValue, maxValue ) );
}
template <typename T = DataType>
typename std::enable_if<is_bool_arithmetic_pair<T>::value>::type setMinValue( const typename T::second_type& minValue )
{
m_minValue = DataType( false, minValue );
}
template <typename T = DataType>
typename std::enable_if<is_bool_arithmetic_pair<T>::value>::type setMaxValue( const typename T::second_type& maxValue )
{
m_maxValue = DataType( false, maxValue );
}
template <typename T = DataType>
typename std::enable_if<is_bool_arithmetic_pair<T>::value>::type clearRange()
{
m_minValue.reset();
m_maxValue.reset();
}
template <typename T = DataType>
typename std::enable_if<is_bool_arithmetic_pair<T>::value>::type clearMinValue()
{
m_minValue.reset();
}
template <typename T = DataType>
typename std::enable_if<is_bool_arithmetic_pair<T>::value>::type clearMaxValue()
{
m_maxValue.reset();
}
// Override validate from PdmFieldHandle
QString validate() const override;
// Clamp value to range if defined
DataType clampValue( const DataType& value ) const
{
if constexpr ( std::is_arithmetic<DataType>::value )
{
DataType clampedValue = value;
if ( m_minValue.has_value() && clampedValue < m_minValue.value() )
{
clampedValue = m_minValue.value();
}
if ( m_maxValue.has_value() && clampedValue > m_maxValue.value() )
{
clampedValue = m_maxValue.value();
}
return clampedValue;
}
else if constexpr ( is_bool_arithmetic_pair<DataType>::value )
{
DataType clampedValue = value;
if ( m_minValue.has_value() && clampedValue.second < m_minValue.value().second )
{
clampedValue.second = m_minValue.value().second;
}
if ( m_maxValue.has_value() && clampedValue.second > m_maxValue.value().second )
{
clampedValue.second = m_maxValue.value().second;
}
return clampedValue;
}
else
{
return value;
}
}
protected:
DataType m_fieldValue;
// Default value stuff.
//
// This is not used enough. Remove when dust has settled.
// ResInsight uses at one point to find whether the value is changed by the user
public:
const DataType& defaultValue() const { return m_defaultFieldValue; }
void setDefaultValue( const DataType& val ) { m_defaultFieldValue = val; }
protected:
DataType m_defaultFieldValue;
// Range validation members
std::optional<DataType> m_minValue;
std::optional<DataType> m_maxValue;
};
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
template <typename DataType>
void caf::PdmDataValueField<DataType>::setValueWithFieldChanged( const DataType& fieldValue )
{
CAF_ASSERT( isInitializedByInitFieldMacro() );
PdmUiFieldHandleInterface* uiFieldHandleInterface = capability<PdmUiFieldHandleInterface>();
if ( uiFieldHandleInterface )
{
QVariant oldValue = uiFieldHandleInterface->toUiBasedQVariant();
m_fieldValue = fieldValue;
QVariant newUiBasedQVariant = uiFieldHandleInterface->toUiBasedQVariant();
uiFieldHandleInterface->notifyFieldChanged( oldValue, newUiBasedQVariant );
}
else
{
m_fieldValue = fieldValue;
}
}
//--------------------------------------------------------------------------------------------------
/// Validates the field value against optional range constraints.
/// Empty QString means valid, non-empty QString contains error message.
//--------------------------------------------------------------------------------------------------
template <typename DataType>
QString caf::PdmDataValueField<DataType>::validate() const
{
if constexpr ( std::is_arithmetic<DataType>::value )
{
if ( m_minValue.has_value() && m_fieldValue < m_minValue.value() )
{
return QString( "Value %1 is below minimum %2" ).arg( m_fieldValue ).arg( m_minValue.value() );
}
if ( m_maxValue.has_value() && m_fieldValue > m_maxValue.value() )
{
return QString( "Value %1 exceeds maximum %2" ).arg( m_fieldValue ).arg( m_maxValue.value() );
}
}
else if constexpr ( is_bool_arithmetic_pair<DataType>::value )
{
if ( m_minValue.has_value() && m_fieldValue.second < m_minValue.value().second )
{
return QString( "Value %1 is below minimum %2" ).arg( m_fieldValue.second ).arg( m_minValue.value().second );
}
if ( m_maxValue.has_value() && m_fieldValue.second > m_maxValue.value().second )
{
return QString( "Value %1 exceeds maximum %2" ).arg( m_fieldValue.second ).arg( m_maxValue.value().second );
}
}
return executeCustomValidation();
}
} // End of namespace caf