I was able to roll my own selection. The logic needed was simple since I only needed to support the selection of a single row.
My ModelItem looks like:
struct ModelItem
{
Q_GADGET
Q_PROPERTY( QString population MEMBER population )
Q_PROPERTY( int averageAge MEMBER averageAge )
Q_PROPERTY( bool selected MEMBER selected )
public:
enum class Role {
Selection = Qt::UserRole,
ColumnType,
ColorValue
};
Q_ENUM(Role)
QString population;
int averageAge;
bool selected { false };
bool operator!=( const ModelItem& other )
{
return other.population != this->population
|| other.averageAge != this->averageAge;
}
};
The key points here are the selected property to hold whether or not an item is selected and the definition of the custom Selection role.
I needed a roleNames function for my custom role
QHash<int, QByteArray>
ModelList::
roleNames() const
{
return {
{ Qt::DisplayRole, "display" },
{ int( ModelItem::Role::Selection ), "selected" },
{ int( ModelItem::Role::ColumnType ), "type" },
{ int( ModelItem::Role::ColorValue ), "colorValue" }
};
}
The key here is that I use the string "selected" to refer to my custom Selection role.
My data function looks like:
QVariant
ModelList::
data( const QModelIndex& index, int role ) const
{
const ModelItem modelItem = mList.at( index.row() );
QVariant result = QVariant();
if ( role == Qt::DisplayRole )
{
if ( index.column() == 0 )
{
result = QVariant( QString( modelItem.population ) );
}
else
{
result = QVariant( QString::number( modelItem.averageAge ) );
}
}
if ( role == int( ModelItem::Role::Selection ) )
{
result = QVariant( QString( modelItem.selected ? "#eeeeee" : "white" ) );
}
if ( role == int( ModelItem::Role::ColumnType ) )
{
if ( index.column() == 0 )
result = QVariant( QString( "stringValue" ) );
else
result = QVariant( QString( "colorValue" ) );
}
if ( role == int( ModelItem::Role::ColorValue ) )
{
QString color;
if ( modelItem.averageAge < 13 )
color = "red";
else if ( modelItem.averageAge < 35 )
color = "yellow";
else
color = "green";
result = QVariant( color );
}
qDebug() << role << " " << result;
return result;
}
The key here is to check to see if the role is the custom Selection role and then return a color based on the value of selected in the modelItem.
The final piece is to have the QML use this custom role:
delegate: DelegateChooser
{
role: "type"
DelegateChoice
{
roleValue: "colorValue"
delegate: Rectangle
{
color: selected
Rectangle
{
color: colorValue
width: parent.height
height: parent.height
radius: width * 0.5;
anchors.horizontalCenter: parent.horizontalCenter;
}
MouseArea
{
anchors.fill: parent
onClicked:
{
var idx = Backend.modelResults.list.index( row, column )
console.log( "Clicked cell: ", idx.row, " ", Backend.modelResults.list.data( idx ) )
Backend.modelResults.list.select( idx.row );
}
}
}
}
DelegateChoice
{
delegate: Rectangle
{
color: selected
Text
{
text: display
anchors.fill: parent
anchors.margins: 10
color: 'black'
font.pixelSize: 15
verticalAlignment: Text.AlignVCenter
}
MouseArea
{
anchors.fill: parent
onClicked:
{
var idx = Backend.modelResults.list.index( row, column )
console.log( "Clicked cell: ", idx.row, " ", Backend.modelResults.list.data( idx ) )
Backend.modelResults.list.select( idx.row );
}
}
}
}
}
The key here is the color: selected part of the Rectangle for the delegate of each DelegateChoice. selected refers to the string selected I setup in the roleNames function above. The system knows to call the data function with the correct ModelItem::Role so if ( role == int( ModelItem::Role::Selection ) ) resolves to true.
For each DelegateChoice, I defined a MouseArea for the cell which calls the select function in the model. The select function is:
void
ModelList::
select( int index )
{
beginResetModel();
for ( int x = 0; x < this->mList.length(); x++ )
{
this->mList[x].selected = ( x == index );
}
endResetModel();
}
The begin/endResetModel causes the table to be redrawn when the selection changes.
The example project has been updated.
Suggested improvement to this solution are welcome.