Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Restore TextInput after invalid input
Forum Updated to NodeBB v4.3 + New Features

Restore TextInput after invalid input

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
9 Posts 3 Posters 189 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E Offline
    E Offline
    ECEC
    wrote last edited by
    #1

    I have a TextInput whose text property is bound to a model role. On successful input, the model role is updated, while invalid inputs are rejected and the model is not updated. In the latter case, I tried setting the text property to its default "--", but of course this broke the binding with the model. I've gotten around this by using a Binding {} to restore the binding after text is assigned to manually, but these seems rather convoluted.

    Is there a cleaner way to programmatically reset the TextInput so that it reverts to whatever is in the model?

    1 Reply Last reply
    0
    • jeremy_kJ Offline
      jeremy_kJ Offline
      jeremy_k
      wrote last edited by jeremy_k
      #2

      For Qt 6.10 (and later presumably): Changing Model Data

      For earlier versions, the delegate can call the model's setData(), which can emit dataChanged() even if the change attempt is rejected. This works for me with Qt 6.9:
      QML:

      ListView {
          model: m
          delegate: TextInput {
              text: display
              onEditingFinished: m.setData(m.index(index, 0), text)
          }
      }
      

      C++

      bool setData(const QModelIndex &index, const QVariant &value, int role) override
      {
          auto updated = (role == Qt::ItemDataRole::EditRole) ?
                         value.toString().toUpper() : value;
          if (role == Qt::ItemDataRole::EditRole && updated.toString().contains('!')) {
              emit dataChanged(index, index, { Qt::ItemDataRole::DisplayRole });
              return false;
          }
          return QStandardItemModel::setData(index, updated, role);
      }
      

      Another option is the Qt Widgets model-view pattern, with a separate editor that is displayed when the delegate instance is activated. On completion of editing, the editor calls setData(). The delegate itself remains bound to model data, and is unaware of intermediate or invalid edits. Exchange more code in the delegate for less code in the model.

      Asking a question about code? http://eel.is/iso-c++/testcase/

      1 Reply Last reply
      3
      • GrecKoG Offline
        GrecKoG Offline
        GrecKo
        Qt Champions 2018
        wrote last edited by
        #3

        onEditingFinished: m.setData(m.index(index, 0), text)

        No need to call setData either in 6.9, you can do it with onEditingFinished: model.display = text.

        jeremy_kJ 1 Reply Last reply
        2
        • GrecKoG GrecKo

          onEditingFinished: m.setData(m.index(index, 0), text)

          No need to call setData either in 6.9, you can do it with onEditingFinished: model.display = text.

          jeremy_kJ Offline
          jeremy_kJ Offline
          jeremy_k
          wrote last edited by
          #4

          @GrecKo said in Restore TextInput after invalid input:

          onEditingFinished: m.setData(m.index(index, 0), text)

          No need to call setData either in 6.9, you can do it with onEditingFinished: model.display = text.

          Indeed. Simplifying further:

          ListView {
              anchors.fill: parent
              model: m
              delegate: TextInput {
                  text: display
                  onEditingFinished: display = text
              }
          }
          
          bool setData(const QModelIndex &index, const QVariant &value, int role) override {
              auto updated = (role == Qt::ItemDataRole::DisplayRole) ? value.toString().toUpper() : value;
              if (role == Qt::ItemDataRole::DisplayRole && updated.toString().contains('!')) {
                  return false;
              }
             return QStandardItemModel::setData(index, updated, role);
          }
          

          Asking a question about code? http://eel.is/iso-c++/testcase/

          1 Reply Last reply
          1
          • GrecKoG Offline
            GrecKoG Offline
            GrecKo
            Qt Champions 2018
            wrote last edited by
            #5

            uh that doesn't modify back the text of the TextInput when the setData is refused unless I'm mistaken.

            E 1 Reply Last reply
            0
            • GrecKoG GrecKo

              uh that doesn't modify back the text of the TextInput when the setData is refused unless I'm mistaken.

              E Offline
              E Offline
              ECEC
              wrote last edited by
              #6

              @GrecKo Indeed, which is why I've had to resort to always emitting dataChanged and never returning false.

              jeremy_kJ 1 Reply Last reply
              0
              • GrecKoG Offline
                GrecKoG Offline
                GrecKo
                Qt Champions 2018
                wrote last edited by
                #7

                note that you could still return false.

                1 Reply Last reply
                0
                • E ECEC

                  @GrecKo Indeed, which is why I've had to resort to always emitting dataChanged and never returning false.

                  jeremy_kJ Offline
                  jeremy_kJ Offline
                  jeremy_k
                  wrote last edited by jeremy_k
                  #8

                  @ECEC said in Restore TextInput after invalid input:

                  @GrecKo Indeed, which is why I've had to resort to always emitting dataChanged and never returning false.

                  I was surprised, but it does return the TextInput to the model's value. Full code:

                  Main.qml

                  import QtQuick
                  
                  Window {
                      visible: true
                      required property QtObject m
                  
                      ListView {
                          anchors.fill: parent
                          model: m
                          delegate: TextInput {
                              text: display
                              onEditingFinished: display = text
                          }
                      }
                  }
                  

                  main.cpp:

                  #include <QGuiApplication>
                  #include <QQmlApplicationEngine>
                  #include <QStandardItemModel>
                  
                  class Model : public QStandardItemModel {
                  public:
                      bool setData(const QModelIndex &index, const QVariant &value, int role) override {
                          auto updated = (role == Qt::ItemDataRole::DisplayRole) ? value.toString().toUpper() : value;
                          if (role == Qt::ItemDataRole::DisplayRole && updated.toString().contains('!')) {
                              return false;
                          }
                  
                          return QStandardItemModel::setData(index, updated, role);
                      }
                  };
                  
                  int main(int argc, char *argv[])
                  {
                      QGuiApplication app(argc, argv);
                  
                      Model model;
                      model.appendRow(new QStandardItem("something"));
                  
                      QQmlApplicationEngine engine;
                      engine.setInitialProperties({ {"m", QVariant::fromValue(&model)} });
                      engine.loadFromModule("untitled1", "Main");
                  
                      return app.exec();
                  }
                  

                  Asking a question about code? http://eel.is/iso-c++/testcase/

                  1 Reply Last reply
                  0
                  • E Offline
                    E Offline
                    ECEC
                    wrote last edited by
                    #9

                    It does make you wonder what setData()'s return value is used for.

                    1 Reply Last reply
                    0

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved