title: Sort the data of a Qml ListView by using a QSortFilterProxyModel.
body: <p>I'm making a chat client, and I am using a qml <code>ListView</code> to display all the messages in a "chat room".</p>
  
  <p>I am using my own model class that derives from <code>QAbstractListModel</code> for storing all the messages in all the rooms that the user is a member of.</p>
  
  <p>I want all the messages sorted based on the timestamp they where originally sent, the newest message should appear on the bottom of the view, and the oldest should be at the top. </p>
  
  <p>I also want the user to be able to filter the messages based on which room they where sent in. Which I've already solved.</p>
  
  <p>Here is the declaration of my custom model</p>
  
  <pre><code>class MessageModel : public QAbstractListModel
  {
  public:
      enum MessageRoles {
              Id = Qt::UserRole + 1,
              RoomId = Qt::UserRole + 2,
              PersonId = Qt::UserRole + 3,
              PersonEmail = Qt::UserRole + 4,
              Created = Qt::UserRole + 5,
              Text = Qt::UserRole + 6
          };
      explicit MessageModel(QObject * parent = nullptr);
      int rowCount(const QModelIndex &amp;parent = QModelIndex()) const override;
      QHash&lt;int, QByteArray&gt; roleNames() const;
      QVariant data(const QModelIndex &amp;index, int role = Qt::DisplayRole) const override; 
      void reset_data(QJsonArray new_data);
      void insert_unique_data(QJsonArray new_data);
  private:
      QList&lt;LocalData::Message&gt; m_data;
  
  };
  </code></pre>
  
  <p>Here is the implementation of my model</p>
  
  <pre><code>MessageModel::MessageModel(QObject *parent) : QAbstractListModel(parent)
  {}
  
  int MessageModel::rowCount(const QModelIndex &amp;parent) const
  {
      return m_data.size();
  }
  
  QHash&lt;int, QByteArray&gt; MessageModel::roleNames() const
  {
      QHash&lt;int, QByteArray&gt; roles;
      roles[Id] = "id";
      roles[RoomId] = "roomId";
      roles[PersonId] = "personId";
      roles[PersonEmail] = "personEmail";
      roles[Created] = "created";
      roles[Text] = "messageText";
      return roles;
  }
  
  QVariant MessageModel::data(const QModelIndex &amp;index, int role) const {
  
      if(!index.isValid())
          return QVariant();
  
      if(index.row() &gt;= m_data.size())
          return QVariant();
  
      switch (role) {
          case Id:
              return m_data.at(index.row()).id;
          case RoomId:
              return m_data.at(index.row()).roomId;
          case PersonId:
              return m_data.at(index.row()).personId;
          case PersonEmail:
              return m_data.at(index.row()).personEmail;
          case Text:
              return m_data.at(index.row()).text;
          case Created:
              return m_data.at(index.row()).created.toString(Qt::ISODateWithMs);
      }
  
      return QVariant();
  }
  
  void MessageModel::update_data(QJsonArray new_data)
  {
      beginResetModel();
      m_data.clear();
  
      foreach (const QJsonValue &amp; val, new_data) {
          m_data.push_back(LocalData::Message(val.toObject()));
      }
      endResetModel();
  }
  
  void MessageModel::insert_unique_data(QJsonArray new_data)
  {
      QList&lt;LocalData::Message&gt; temp;
      foreach (const QJsonValue &amp; val, new_data) {
          auto obj_to_insert = LocalData::Message(val.toObject());
          if(!m_data.contains(obj_to_insert))
              temp.push_back(obj_to_insert);
      }
      int begin = rowCount();
      int end = rowCount() + temp.size() - 1;
  
      beginInsertRows(QModelIndex(), begin, end);
      foreach (const LocalData::Message &amp; msg, temp) {
          m_data.push_back(msg);
      }
      endInsertRows();
  }
  </code></pre>
  
  <p>I want to use a <code>QSortFilterProxyModel</code> to both sort and filter the messages.
  I've managed to make it filter the messages correctly based on the <code>RoomId</code> role, but I am having troubles with sorting the messages correctly on the <code>Created</code> role. </p>
  
  <p>The proxy model that I'm using is very simple, I've just tried to override the lessThan() function like so:</p>
  
  <pre><code>bool SortFilterProxyModel::lessThan(const QModelIndex &amp;source_left, const QModelIndex &amp;source_right) const
      {
          QVariant leftData = sourceModel()-&gt;data(source_left);
          QVariant rightData = sourceModel()-&gt;data(source_right);
  
          if(leftData.type() == QVariant::DateTime)
          {
              return leftData.toDateTime() &lt; rightData.toDateTime();
          }
          else {
              return leftData.toString() &lt; rightData.toString();
          }
      }
  </code></pre>
  
  <p>Othervise it is just a plain <code>QSortFilterProxyModel</code>.
  When it's being initialised I am calling the following functions:</p>
  
  <pre><code>m_messageModel = new MessageModel;
  m_messageProxyModel = new SortFilterProxyModel;
  
  m_messageProxyModel-&gt;setSourceModel(qobject_cast&lt;QAbstractListModel *&gt;(m_messageModel));
  
  m_engine.rootContext()-&gt;setContextProperty("messages", m_messageProxyModel);
  
  m_messageProxyModel.setSortRole(MessageModel::Created);   
  
  m_messageProxyModel.setDynamicSortFilter(true); 
  
  m_messageProxyModel.sort(0, Qt::AscendingOrder);
  </code></pre>
  
  <p><code>m_messageModel</code>, <code>m_messageProxyModel</code>, and <code>m_engine</code> is all member variables (pointers) declared in a class which is instantiated in <code>main()</code>.
  m_engine is a <code>QQmlApplicationEngine</code> which exposes the variable to qml.</p>
  
  <p>Here is the qml file containing the ListView with all the messages. </p>
  
  <pre><code>import QtQuick 2.0
  import QtQuick.Controls 1.4
  
  Rectangle{
      property alias messageView: _messagePanelView
      Component {
          id: _messagePanelDelegate
          Item{
              id: _messagePanelDelegateItem;
  
              width: root.width * 0.8
              height: _messagePanelMessageColumn.height
              Column{
                  id: _messagePanelMessageColumn;
                  height: children.height;
                  spacing: 20
                  Text{ text: "&lt;b&gt;" + personEmail + "&lt;/b&gt; &lt;t /&gt;" + created; font.pointSize: 7 + Math.log(root.width) }
                  Text{ text: messageText }
                  Rectangle{
                      width: parent.width
                      height: 20
                      color: "#FFFFFF"
                  }
              }
          }
      }
      ScrollView{
          anchors.fill: parent
          ListView {
              id: _messagePanelView
              height: root.height - 100
              model: messages
              delegate: _messagePanelDelegate
              interactive: true
          }
      }
  }
  </code></pre>
  
  <p>The source model is currently populated with data when the user asks for all messages in a specific "chat room". A function similar to this is called when the user presses a button.</p>
  
  <pre><code>void sync_messages_and_filter_based_on_roomId(QString roomId)
  {
      m_messageProxyModel-&gt;setFilterRole(MessageModel::RoomId);
      m_messageProxyModel-&gt;setFilterCaseSensitivity(Qt::CaseInsensitive);
      m_messageProxyModel-&gt;setFilterRegExp(QRegExp::escape(roomId));
  
      fetch_messages_in_room_from_server_async(roomId); 
  }
  </code></pre>
  
  <p><code>fetch_messages_in_room_from_server_async(roomId);</code> is a function that asks the server for new messages, receives a JSON formatted response payload, creates a <code>QJsonArray</code> object and calls <code>void MessageModel::insert_unique_data(QJsonArray new_data)</code> All of which happens in a seperate worker thread.</p>
  
  <p>When this is called the first time, the data actually gets sorted in an descending order (oldest at the bottom, most recent at the top). But when it's called a second time, when the server has more data to provide, the client is supposed to insert all the new messages as rows in the source model, and have the proxy model sort it in an ascending order (most recent at the bottom, oldest at the top) once the new data has been inserted.</p>
  
  <p>Currently the model is only being sorted in an descending order, the first time data is inserted into the model. But once the model has been updated for the first time, it's no longer sorting the data, and the new messages appear on the bottom of the <code>ListView</code> instead of the top, which means that the proxy model has stopped sorting the model.</p>
  
  <p><a href="https://i.stack.imgur.com/rWNug.png" rel="noreferrer">Here is an image of the ListView, after inserting a second time. Notice the timestamps</a>
  <a href="https://i.stack.imgur.com/SZ3mM.png" rel="noreferrer">This is what happens when I terminate the program, and insert all the messages at once</a></p>
  
  <p>The result that I want is to have the proxy model sort the messages in an ascending order (most recent at the botton, oldest at the top) every time the source model is updated/changed. Meaning when <code>void MessageModel::insert_unique_data(QJsonArray new_data)</code> is called, I'm expecting the new messages to appear on the bottom, with the most recent message as the last message.</p>
  
  <p>Thank you for taking your time to read through my insanely long post. I just wanted to cover all the details since exposing c++ models to qml requires a lot of steps.</p>