QskIcon/QskLabelData introduced

This commit is contained in:
Uwe Rathmann 2023-03-09 15:30:40 +01:00
parent 10af58137f
commit f9f5de8eb0
8 changed files with 527 additions and 144 deletions

102
src/common/QskLabelData.cpp Normal file
View File

@ -0,0 +1,102 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskLabelData.h"
#include "QskGraphicProvider.h"
#include <qvariant.h>
#include <qhashfunctions.h>
static void qskRegisterLabelData()
{
qRegisterMetaType< QskLabelData >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskLabelData >();
#endif
}
Q_CONSTRUCTOR_FUNCTION( qskRegisterLabelData )
QskLabelData::QskLabelData( const QString& text )
: m_text( text )
{
}
QskLabelData::QskLabelData( const QskIcon& icon )
: m_icon( icon )
{
}
QskLabelData::QskLabelData( const QString& text, const QskIcon& icon )
: m_text( text )
, m_icon( icon )
{
}
bool QskLabelData::operator==( const QskLabelData& other ) const noexcept
{
return ( m_text == other.m_text )
&& ( m_icon == other.m_icon );
}
void QskLabelData::setText( const QString& text )
{
m_text = text;
}
void QskLabelData::setIconSource( const QUrl& source )
{
m_icon.setSource( source );
}
void QskLabelData::setIcon( const QskIcon& icon )
{
m_icon = icon;
}
QskHashValue QskLabelData::hash( QskHashValue seed ) const
{
const auto hash = qHash( m_text, seed );
return m_icon.hash( hash );
}
#ifndef QT_NO_DEBUG_STREAM
#include <qdebug.h>
QDebug operator<<( QDebug debug, const QskLabelData& labelData )
{
QDebugStateSaver saver( debug );
debug.nospace();
const auto icon = labelData.icon();
const auto text = labelData.text();
debug << "Label" << "( ";
if ( !text.isEmpty() )
{
debug << text;
if ( !icon.isNull() )
debug << ", ";
}
if ( !icon.source().isEmpty() )
{
debug << icon.source();
}
else if ( !icon.maybeGraphic().isNull() )
{
debug << "I:" << icon.maybeGraphic().hash( 0 );
}
debug << " )";
return debug;
}
#endif
#include "moc_QskLabelData.cpp"

79
src/common/QskLabelData.h Normal file
View File

@ -0,0 +1,79 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_LABEL_DATA_H
#define QSK_LABEL_DATA_H
#include "QskGlobal.h"
#include "QskIcon.h"
#include <qstring.h>
#include <qmetatype.h>
#include <qdebug.h>
class QSK_EXPORT QskLabelData
{
Q_GADGET
Q_PROPERTY( QString text READ text WRITE setText )
Q_PROPERTY( QUrl iconSource READ iconSource WRITE setIconSource )
public:
QskLabelData() = default;
QskLabelData( const QString& );
QskLabelData( const QskIcon& );
QskLabelData( const QString&, const QskIcon& );
bool operator==( const QskLabelData& ) const noexcept;
bool operator!=( const QskLabelData& ) const noexcept;
void setText( const QString& );
QString text() const noexcept;
void setIconSource( const QUrl& );
QUrl iconSource() const noexcept;
void setIcon( const QskIcon& );
QskIcon icon() const noexcept;
QskHashValue hash( QskHashValue ) const;
private:
QString m_text;
QskIcon m_icon;
};
Q_DECLARE_METATYPE( QskLabelData )
inline QString QskLabelData::text() const noexcept
{
return m_text;
}
inline QskIcon QskLabelData::icon() const noexcept
{
return m_icon;
}
inline QUrl QskLabelData::iconSource() const noexcept
{
return m_icon.source();
}
inline bool QskLabelData::operator!=( const QskLabelData& other ) const noexcept
{
return ( !( *this == other ) );
}
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
QSK_EXPORT QDebug operator<<( QDebug, const QskLabelData& );
#endif
#endif

View File

@ -5,8 +5,7 @@
#include "QskSegmentedBar.h"
#include "QskGraphic.h"
#include "QskGraphicProvider.h"
#include "QskLabelData.h"
#include "QskTextOptions.h"
#include "QskEvent.h"
#include "QskSkinlet.h"
@ -27,33 +26,6 @@ QSK_SYSTEM_STATE( QskSegmentedBar, Selected, QskAspect::FirstSystemState << 1 )
QSK_SYSTEM_STATE( QskSegmentedBar, Minimum, QskAspect::FirstSystemState << 2 )
QSK_SYSTEM_STATE( QskSegmentedBar, Maximum, QskAspect::FirstSystemState << 3 )
namespace
{
class Option
{
public:
Option() = default;
Option( const QUrl& graphicSource, const QString& text )
: graphicSource( graphicSource )
, text( text )
{
#if 1
// lazy loading TODO ...
if ( !graphicSource.isEmpty() )
graphic = Qsk::loadGraphic( graphicSource );
#endif
}
QUrl graphicSource;
QString text;
QskGraphic graphic;
bool isEnabled = true;
};
}
class QskSegmentedBar::PrivateData
{
public:
@ -62,20 +34,8 @@ class QskSegmentedBar::PrivateData
{
}
void addOption( QskSegmentedBar* bar, const Option& option )
{
this->options += option;
bar->resetImplicitSize();
bar->update();
Q_EMIT bar->countChanged();
if ( this->options.count() == 1 )
bar->setSelectedIndex( 0 );
}
QVector< Option > options;
QVector< QskLabelData > options;
QVector< bool > enabled;
int selectedIndex = -1;
int currentIndex = -1;
@ -139,24 +99,39 @@ QskTextOptions QskSegmentedBar::textOptions() const
int QskSegmentedBar::addOption( const QUrl& graphicSource, const QString& text )
{
m_data->addOption( this, Option( graphicSource, text ) );
return addOption( QskLabelData( text, graphicSource ) );
}
int QskSegmentedBar::addOption( const QskLabelData& option )
{
m_data->options += option;
m_data->enabled += true;
resetImplicitSize();
update();
Q_EMIT optionsChanged();
if ( count() == 1 )
setSelectedIndex( 0 );
return count() - 1;
}
QVariantList QskSegmentedBar::optionAt( int index ) const
QskLabelData QskSegmentedBar::optionAt( int index ) const
{
const auto& options = m_data->options;
return m_data->options.value( index );
}
if( index < 0 || index >= options.count() )
return QVariantList();
void QskSegmentedBar::setOptions( const QVector< QskLabelData >& options )
{
m_data->options = options;
m_data->enabled.fill( true, options.count() );
const auto& option = options[ index ];
resetImplicitSize();
update();
QVariantList list;
list += QVariant::fromValue( option.graphic );
list += QVariant::fromValue( option.text );
return list;
Q_EMIT optionsChanged();
}
QskAspect::Variation QskSegmentedBar::effectiveVariation() const
@ -168,21 +143,26 @@ void QskSegmentedBar::mousePressEvent( QMouseEvent* event )
{
const int index = indexAtPosition( qskMousePosition( event ) );
if( index < 0 || index >= count() || !m_data->options[ index ].isEnabled )
return;
m_data->isPressed = true;
if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
if( isSegmentEnabled( index ) )
{
if( !QGuiApplication::styleHints()->setFocusOnTouchRelease() )
m_data->isPressed = true;
if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
{
if( index != m_data->currentIndex )
setCurrentIndex( index );
if( !QGuiApplication::styleHints()->setFocusOnTouchRelease() )
{
if( index != m_data->currentIndex )
setCurrentIndex( index );
}
}
}
}
void QskSegmentedBar::mouseUngrabEvent()
{
m_data->isPressed = false;
}
void QskSegmentedBar::mouseReleaseEvent( QMouseEvent* event )
{
int index = -1;
@ -193,15 +173,15 @@ void QskSegmentedBar::mouseReleaseEvent( QMouseEvent* event )
index = indexAtPosition( qskMousePosition( event ) );
}
if( index < 0 || !m_data->options[ index ].isEnabled )
return;
if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
if( isSegmentEnabled( index ) )
{
if( QGuiApplication::styleHints()->setFocusOnTouchRelease() )
if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
{
if( index != m_data->currentIndex )
setCurrentIndex( index );
if( QGuiApplication::styleHints()->setFocusOnTouchRelease() )
{
if( index != m_data->currentIndex )
setCurrentIndex( index );
}
}
}
@ -310,7 +290,7 @@ void QskSegmentedBar::clear()
return;
m_data->options.clear();
Q_EMIT countChanged();
Q_EMIT optionsChanged();
if( m_data->selectedIndex >= 0 )
{
@ -329,13 +309,8 @@ void QskSegmentedBar::clear()
void QskSegmentedBar::setCurrentIndex( int index )
{
const auto& options = m_data->options;
if( ( index < 0 ) || ( index >= options.count() )
|| !options[ index ].isEnabled )
{
if ( !isSegmentEnabled( index ) )
index = -1;
}
if( index != m_data->currentIndex )
{
@ -351,14 +326,8 @@ int QskSegmentedBar::currentIndex() const
void QskSegmentedBar::setSelectedIndex( int index )
{
if( index < 0 || index >= m_data->options.count() )
{
if ( !isSegmentEnabled( index ) )
index = -1;
}
else if ( !m_data->options[ index ].isEnabled )
{
index = -1; // ???
}
if( index != m_data->selectedIndex )
{
@ -400,7 +369,7 @@ int QskSegmentedBar::nextIndex( int index, bool forwards ) const
while( ++index < count )
{
if( options[ index ].isEnabled )
if( isSegmentEnabled( index ) )
return index;
}
}
@ -411,7 +380,7 @@ int QskSegmentedBar::nextIndex( int index, bool forwards ) const
while( --index >= 0 )
{
if( options[ index ].isEnabled )
if( isSegmentEnabled( index ) )
return index;
}
}
@ -419,6 +388,11 @@ int QskSegmentedBar::nextIndex( int index, bool forwards ) const
return -1;
}
QVector< QskLabelData > QskSegmentedBar::options() const
{
return m_data->options;
}
int QskSegmentedBar::count() const
{
return m_data->options.count();
@ -426,15 +400,15 @@ int QskSegmentedBar::count() const
void QskSegmentedBar::setSegmentEnabled( int index, bool enabled )
{
auto& options = m_data->options;
auto& bitVector = m_data->enabled;
if( ( index < 0 ) || ( index >= options.count() )
|| ( options[ index ].isEnabled == enabled ) )
if( ( index < 0 ) || ( index >= bitVector.count() )
|| ( bitVector[ index ] == enabled ) )
{
return;
}
options[ index ].isEnabled = enabled;
bitVector[ index ] = enabled;
if( !enabled )
{
@ -447,12 +421,7 @@ void QskSegmentedBar::setSegmentEnabled( int index, bool enabled )
bool QskSegmentedBar::isSegmentEnabled( int index ) const
{
const auto& options = m_data->options;
if( index < 0 || index >= options.count() )
return false;
return options[ index ].isEnabled;
return m_data->enabled.value( index, false );
}
int QskSegmentedBar::indexAtPosition( const QPointF& pos ) const

View File

@ -13,6 +13,7 @@
class QskTextOptions;
class QskGraphic;
class QskLabelData;
class QSK_EXPORT QskSegmentedBar : public QskControl
{
@ -21,13 +22,16 @@ class QSK_EXPORT QskSegmentedBar : public QskControl
Q_PROPERTY( Qt::Orientation orientation READ orientation
WRITE setOrientation NOTIFY orientationChanged )
Q_PROPERTY( QVector< QskLabelData > options READ options
WRITE setOptions NOTIFY optionsChanged )
Q_PROPERTY( int selectedIndex READ selectedIndex
WRITE setSelectedIndex NOTIFY selectedIndexChanged USER true )
Q_PROPERTY( int currentIndex READ currentIndex
WRITE setCurrentIndex NOTIFY currentIndexChanged )
Q_PROPERTY( int count READ count NOTIFY countChanged )
Q_PROPERTY( int count READ count )
using Inherited = QskControl;
@ -47,6 +51,12 @@ class QSK_EXPORT QskSegmentedBar : public QskControl
QskTextOptions textOptions() const;
int addOption( const QUrl&, const QString& );
int addOption( const QskLabelData& );
void setOptions( const QVector< QskLabelData >& );
QVector< QskLabelData > options() const;
QskLabelData optionAt( int ) const;
void clear();
@ -55,8 +65,6 @@ class QSK_EXPORT QskSegmentedBar : public QskControl
int count() const;
QVariantList optionAt( int ) const;
void setSegmentEnabled( int, bool );
bool isSegmentEnabled( int ) const;
@ -72,12 +80,13 @@ class QSK_EXPORT QskSegmentedBar : public QskControl
Q_SIGNALS:
void selectedIndexChanged( int );
void currentIndexChanged( int );
void countChanged();
void optionsChanged();
void orientationChanged();
protected:
void mouseReleaseEvent( QMouseEvent* ) override;
void mousePressEvent( QMouseEvent* ) override;
void mouseReleaseEvent( QMouseEvent* ) override;
void mouseUngrabEvent() override;
void keyPressEvent( QKeyEvent* ) override;
void keyReleaseEvent( QKeyEvent* ) override;

View File

@ -6,6 +6,7 @@
#include "QskSegmentedBarSkinlet.h"
#include "QskSegmentedBar.h"
#include "QskLabelData.h"
#include "QskGraphic.h"
#include "QskColorFilter.h"
#include "QskFunctions.h"
@ -18,42 +19,24 @@
namespace
{
#if 1 // unify with the implementation from QskMenu
template< class T >
static inline QVariant qskSampleAt( const QskSegmentedBar* bar, int index )
{
const auto list = bar->optionAt( index );
for ( const auto& value : list )
{
if ( value.canConvert< T >() )
return value;
}
return QVariant();
}
template< class T >
static inline T qskValueAt( const QskSegmentedBar* bar, int index )
{
const auto sample = qskSampleAt< T >( bar, index );
return sample.template value< T >();
}
#endif
QskGraphic iconAt( const QskSegmentedBar* bar, const int index )
{
using Q = QskSegmentedBar;
if ( bar->selectedIndex() == index )
{
// f.e Material 3 replaces the icon of the selected element by a checkmark
/*
Material 3 replaces the icon of the selected element by a checkmark,
when icon and text are set. So this code is actually not correct
as it also replaces the icon when there is no text
*/
const auto icon = bar->symbolHint( Q::Icon | Q::Selected );
if ( !icon.isNull() )
return icon;
}
return qskValueAt< QskGraphic >( bar, index );
return bar->optionAt( index ).icon().graphic();
}
class LayoutEngine : public QskSubcontrolLayoutEngine
@ -66,8 +49,9 @@ namespace
setSpacing( bar->spacingHint( Q::Panel ) );
setGraphicTextElements( bar,
Q::Text, qskValueAt< QString >( bar, index ),
const auto option = bar->optionAt( index );
setGraphicTextElements( bar, Q::Text, option.text(),
Q::Icon, iconAt( bar, index ).defaultSize() );
if( bar->orientation() == Qt::Horizontal )
@ -228,33 +212,31 @@ QSizeF QskSegmentedBarSkinlet::segmentSizeHint(
{
using Q = QskSegmentedBar;
QSizeF sizeMax;
const QSizeF sizeSymbol =
bar->symbolHint( Q::Icon | Q::Selected ).defaultSize();
const auto graphic0 = bar->symbolHint( Q::Icon | Q::Selected );
QSizeF segmentSize;
for ( int i = 0; i < bar->count(); i++ )
{
const auto option = bar->optionAt( i );
auto iconSize = option.icon().graphic().defaultSize();
iconSize = iconSize.expandedTo( sizeSymbol );
LayoutEngine layoutEngine( bar, i );
auto graphic = qskValueAt< QskGraphic >( bar, i );
if ( graphic.isNull() )
graphic = graphic0;
layoutEngine.setGraphicTextElements( bar,
Q::Text, qskValueAt< QString >( bar, i ),
Q::Icon, graphic.defaultSize() );
Q::Text, option.text(), Q::Icon, iconSize );
const auto size = layoutEngine.sizeHint( which, QSizeF() );
if( size.width() > sizeMax.width() )
sizeMax = size;
segmentSize = segmentSize.expandedTo( size );
}
sizeMax = bar->outerBoxSize( Q::Segment, sizeMax );
sizeMax = sizeMax.expandedTo( bar->strutSizeHint( Q::Segment ) );
sizeMax = sizeMax.grownBy( bar->marginHint( Q::Segment ) );
segmentSize = bar->outerBoxSize( Q::Segment, segmentSize );
segmentSize = segmentSize.expandedTo( bar->strutSizeHint( Q::Segment ) );
segmentSize = segmentSize.grownBy( bar->marginHint( Q::Segment ) );
return sizeMax;
return segmentSize;
}
QSizeF QskSegmentedBarSkinlet::sizeHint( const QskSkinnable* skinnable,
@ -324,6 +306,7 @@ QRectF QskSegmentedBarSkinlet::sampleRect( const QskSkinnable* skinnable,
LayoutEngine layoutEngine( bar, index );
layoutEngine.setGeometries( rect );
return layoutEngine.subControlRect( subControl );
}
@ -373,7 +356,7 @@ QSGNode* QskSegmentedBarSkinlet::updateSampleNode( const QskSkinnable* skinnable
if ( subControl == Q::Text )
{
const auto text = qskValueAt< QString >( bar, index );
const auto text = bar->optionAt( index ).text();
if( !text.isEmpty() )
{

133
src/graphic/QskIcon.cpp Normal file
View File

@ -0,0 +1,133 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskIcon.h"
#include "QskGraphicProvider.h"
#include <qvariant.h>
#include <qhashfunctions.h>
static void qskRegisterIcon()
{
qRegisterMetaType< QskIcon >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskIcon >();
#endif
}
Q_CONSTRUCTOR_FUNCTION( qskRegisterIcon )
QskIcon::QskIcon()
: m_data( new Data() )
{
}
QskIcon::QskIcon( const QUrl& source )
: m_source( source )
, m_data( new Data() )
{
}
QskIcon::QskIcon( const QskGraphic& graphic )
: m_data( new Data() )
{
if ( !graphic.isNull() )
m_data->graphic = new QskGraphic( graphic );
}
bool QskIcon::operator==( const QskIcon& other ) const noexcept
{
if ( m_data == other.m_data )
return true; // copy
if ( !m_source.isEmpty() || !other.m_source.isEmpty() )
return m_source == other.m_source;
if ( m_data->graphic && other.m_data->graphic )
return m_data->graphic == other.m_data->graphic;
return false;
}
void QskIcon::setSource( const QUrl& source )
{
if ( source != m_source )
{
m_source = source;
#if 1
m_data.detach(); // needed ???
#endif
m_data.reset();
}
}
void QskIcon::setGraphic( const QskGraphic& graphic )
{
m_source.clear();
m_data.detach();
if ( m_data->graphic )
*m_data->graphic = graphic;
else
m_data->graphic = new QskGraphic( graphic );
}
QskGraphic QskIcon::graphic() const
{
if ( m_data->graphic == nullptr && !m_source.isEmpty() )
{
/*
not detaching: lazy loading should affect all copies
note: even when loadGraphic returns a null graphic we
initialize m_iconData to avoid, that loading will
be repeated again and again.
*/
m_data->graphic = new QskGraphic( Qsk::loadGraphic( m_source ) );
}
return m_data->graphic ? *m_data->graphic : QskGraphic();
}
QskHashValue QskIcon::hash( QskHashValue seed ) const
{
if ( !m_source.isEmpty() )
return qHash( m_source, seed );
return maybeGraphic().hash( seed );
}
#ifndef QT_NO_DEBUG_STREAM
#include <qdebug.h>
QDebug operator<<( QDebug debug, const QskIcon& icon )
{
QDebugStateSaver saver( debug );
debug.nospace();
debug << "Icon" << "( ";
if ( !icon.source().isEmpty() )
{
debug << icon.source();
}
else
{
const auto graphic = icon.maybeGraphic();
if ( !graphic.isNull() )
debug << "I:" << graphic.hash( 0 );
}
debug << " )";
return debug;
}
#endif
#include "moc_QskIcon.cpp"

104
src/graphic/QskIcon.h Normal file
View File

@ -0,0 +1,104 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_ICON_H
#define QSK_ICON_H
#include "QskGlobal.h"
#include "QskGraphic.h"
#include <qurl.h>
#include <qmetatype.h>
#include <qshareddata.h>
/*
Most controls offer 2 way to pass an icon:
- URL
- QskGraphic
For the vast majority of the application icons URLs are used
that will be loaded at runtime by a QskGraphicProvider.
( QML code always uses URLs )
QskIcon implements a lazy loading strategy, that avoids unsatisfying
startup performance from loading to many icons in advance.
*/
class QSK_EXPORT QskIcon
{
Q_GADGET
Q_PROPERTY( QskGraphic graphic READ graphic WRITE setGraphic )
Q_PROPERTY( QUrl source READ source WRITE setSource )
public:
QskIcon();
QskIcon( const QUrl& );
QskIcon( const QskGraphic& );
bool operator==( const QskIcon& ) const noexcept;
bool operator!=( const QskIcon& ) const noexcept;
bool isNull() const;
void setSource( const QUrl& );
QUrl source() const noexcept;
void setGraphic( const QskGraphic& );
QskGraphic graphic() const;
QskGraphic maybeGraphic() const noexcept;
QskHashValue hash( QskHashValue ) const;
private:
QUrl m_source;
class Data : public QSharedData
{
public:
~Data() { delete graphic; }
QskGraphic* graphic = nullptr;
};
mutable QExplicitlySharedDataPointer< Data > m_data;
};
Q_DECLARE_METATYPE( QskIcon )
inline bool QskIcon::isNull() const
{
if ( m_data->graphic )
return m_data->graphic->isNull();
return m_source.isEmpty();
}
inline QUrl QskIcon::source() const noexcept
{
return m_source;
}
inline QskGraphic QskIcon::maybeGraphic() const noexcept
{
return m_data->graphic ? *m_data->graphic : QskGraphic();
}
inline bool QskIcon::operator!=( const QskIcon& other ) const noexcept
{
return ( !( *this == other ) );
}
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
QSK_EXPORT QDebug operator<<( QDebug, const QskIcon& );
#endif
#endif

View File

@ -30,6 +30,7 @@ HEADERS += \
common/QskGradientStop.h \
common/QskHctColor.h \
common/QskIntervalF.h \
common/QskLabelData.h \
common/QskMargins.h \
common/QskMetaFunction.h \
common/QskMetaFunction.hpp \
@ -60,6 +61,7 @@ SOURCES += \
common/QskGradientStop.cpp \
common/QskHctColor.cpp \
common/QskIntervalF.cpp \
common/QskLabelData.cpp \
common/QskMargins.cpp \
common/QskMetaFunction.cpp \
common/QskMetaInvokable.cpp \
@ -83,6 +85,7 @@ HEADERS += \
graphic/QskGraphicProvider.h \
graphic/QskGraphicProviderMap.h \
graphic/QskGraphicTextureFactory.h \
graphic/QskIcon.h \
graphic/QskPainterCommand.h \
graphic/QskStandardSymbol.h
@ -95,6 +98,7 @@ SOURCES += \
graphic/QskGraphicProvider.cpp \
graphic/QskGraphicProviderMap.cpp \
graphic/QskGraphicTextureFactory.cpp \
graphic/QskIcon.cpp \
graphic/QskPainterCommand.cpp \
graphic/QskStandardSymbol.cpp