Improve editing fields in Qt Quick GUI
This commit is contained in:
parent
cc78072ffc
commit
d8b6aeb818
|
@ -58,6 +58,7 @@ QHash<int, QByteArray> FieldModel::roleNames() const
|
||||||
{ FieldModelRoles::Key, "key" },
|
{ FieldModelRoles::Key, "key" },
|
||||||
{ FieldModelRoles::Value, "value" },
|
{ FieldModelRoles::Value, "value" },
|
||||||
{ FieldModelRoles::IsPassword, "isPassword" },
|
{ FieldModelRoles::IsPassword, "isPassword" },
|
||||||
|
{ FieldModelRoles::AlwaysActualValue, "actualValue" },
|
||||||
};
|
};
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
@ -112,6 +113,8 @@ QVariant FieldModel::data(const QModelIndex &index, int role) const
|
||||||
|| (*m_fields)[static_cast<size_t>(index.row())].type() != FieldType::Password)
|
|| (*m_fields)[static_cast<size_t>(index.row())].type() != FieldType::Password)
|
||||||
? QString::fromStdString((*m_fields)[static_cast<size_t>(index.row())].value())
|
? QString::fromStdString((*m_fields)[static_cast<size_t>(index.row())].value())
|
||||||
: QString((*m_fields)[static_cast<size_t>(index.row())].value().size(), QChar(0x2022));
|
: QString((*m_fields)[static_cast<size_t>(index.row())].value().size(), QChar(0x2022));
|
||||||
|
case AlwaysActualValue:
|
||||||
|
return QString::fromStdString((*m_fields)[static_cast<size_t>(index.row())].value());
|
||||||
case IsPassword:
|
case IsPassword:
|
||||||
return (*m_fields)[static_cast<size_t>(index.row())].type() == FieldType::Password;
|
return (*m_fields)[static_cast<size_t>(index.row())].type() == FieldType::Password;
|
||||||
default:;
|
default:;
|
||||||
|
@ -169,11 +172,11 @@ bool FieldModel::setData(const QModelIndex &index, const QVariant &value, int ro
|
||||||
switch (index.column()) {
|
switch (index.column()) {
|
||||||
case 0:
|
case 0:
|
||||||
m_fields->at(index.row()).setName(value.toString().toStdString());
|
m_fields->at(index.row()).setName(value.toString().toStdString());
|
||||||
roles << Qt::EditRole << Key;
|
roles << Qt::DisplayRole << Qt::EditRole << Key;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
m_fields->at(index.row()).setValue(value.toString().toStdString());
|
m_fields->at(index.row()).setValue(value.toString().toStdString());
|
||||||
roles << Qt::EditRole << Value;
|
roles << Qt::DisplayRole << Qt::EditRole << Value << AlwaysActualValue;
|
||||||
break;
|
break;
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
@ -189,15 +192,16 @@ bool FieldModel::setData(const QModelIndex &index, const QVariant &value, int ro
|
||||||
}
|
}
|
||||||
case Key:
|
case Key:
|
||||||
m_fields->at(index.row()).setName(value.toString().toStdString());
|
m_fields->at(index.row()).setName(value.toString().toStdString());
|
||||||
roles << Qt::EditRole << Key;
|
roles << Qt::DisplayRole << Qt::EditRole << Key;
|
||||||
break;
|
break;
|
||||||
case Value:
|
case Value:
|
||||||
|
case AlwaysActualValue:
|
||||||
m_fields->at(index.row()).setValue(value.toString().toStdString());
|
m_fields->at(index.row()).setValue(value.toString().toStdString());
|
||||||
roles << Qt::EditRole << Value;
|
roles << Qt::DisplayRole << Qt::EditRole << Value << AlwaysActualValue;
|
||||||
break;
|
break;
|
||||||
case IsPassword:
|
case IsPassword:
|
||||||
m_fields->at(index.row()).setType(value.toBool() ? FieldType::Password : FieldType::Normal);
|
m_fields->at(index.row()).setType(value.toBool() ? FieldType::Password : FieldType::Normal);
|
||||||
roles << FieldTypeRole << IsPassword;
|
roles << Qt::DisplayRole << FieldTypeRole << IsPassword;
|
||||||
break;
|
break;
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
@ -218,14 +222,14 @@ bool FieldModel::setData(const QModelIndex &index, const QVariant &value, int ro
|
||||||
m_fields->emplace_back(m_accountEntry);
|
m_fields->emplace_back(m_accountEntry);
|
||||||
m_fields->back().setName(value.toString().toStdString());
|
m_fields->back().setName(value.toString().toStdString());
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
roles << role;
|
roles << Qt::DisplayRole << Qt::EditRole;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
beginInsertRows(index.parent(), rowCount(), rowCount());
|
beginInsertRows(index.parent(), rowCount(), rowCount());
|
||||||
m_fields->emplace_back(m_accountEntry);
|
m_fields->emplace_back(m_accountEntry);
|
||||||
m_fields->back().setValue(value.toString().toStdString());
|
m_fields->back().setValue(value.toString().toStdString());
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
roles << role;
|
roles << Qt::DisplayRole << Qt::EditRole;
|
||||||
break;
|
break;
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
@ -237,19 +241,6 @@ bool FieldModel::setData(const QModelIndex &index, const QVariant &value, int ro
|
||||||
if (roles.isEmpty()) {
|
if (roles.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// some roles affect other roles
|
|
||||||
switch (role) {
|
|
||||||
case Qt::EditRole:
|
|
||||||
case Key:
|
|
||||||
case Value:
|
|
||||||
roles << Qt::DisplayRole;
|
|
||||||
break;
|
|
||||||
case FieldTypeRole:
|
|
||||||
case IsPassword:
|
|
||||||
roles << Qt::DisplayRole << Qt::EditRole << Key;
|
|
||||||
break;
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
// emit data changed signal on sucess
|
// emit data changed signal on sucess
|
||||||
emit dataChanged(index, index, roles);
|
emit dataChanged(index, index, roles);
|
||||||
return true;
|
return true;
|
||||||
|
@ -324,6 +315,40 @@ bool FieldModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FieldModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
|
||||||
|
{
|
||||||
|
// FIXME: implement an undo/redo command
|
||||||
|
|
||||||
|
// validate input parameter
|
||||||
|
if (sourceParent.isValid() || destinationParent.isValid() || sourceRow < 0 || count <= 0 || destinationChild < 0
|
||||||
|
|| static_cast<size_t>(sourceRow + count) > m_fields->size() || static_cast<size_t>(destinationChild) >= m_fields->size()
|
||||||
|
|| (destinationChild >= sourceRow && destinationChild < (sourceRow + count))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// begin the move
|
||||||
|
if (destinationChild > sourceRow) {
|
||||||
|
// move rows down: the third param is still counted in the initial array!
|
||||||
|
beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild + count);
|
||||||
|
} else {
|
||||||
|
// move rows up
|
||||||
|
beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reserve space for temporary copies (FIXME: possible to avoid this?)
|
||||||
|
m_fields->reserve(m_fields->size() + static_cast<size_t>(count));
|
||||||
|
vector<Io::Field> tmp(static_cast<size_t>(count));
|
||||||
|
// move rows to temporary array
|
||||||
|
move(m_fields->begin() + sourceRow, m_fields->begin() + sourceRow + count, tmp.begin());
|
||||||
|
// erase slots of rows to be moved
|
||||||
|
m_fields->erase(m_fields->begin() + sourceRow, m_fields->begin() + sourceRow + count);
|
||||||
|
// insert rows again at their new position
|
||||||
|
m_fields->insert(m_fields->begin() + destinationChild, tmp.begin(), tmp.end());
|
||||||
|
|
||||||
|
endMoveRows();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool FieldModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
|
bool FieldModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
|
||||||
{
|
{
|
||||||
if (!QAbstractTableModel::dropMimeData(data, action, row, column, parent) && data->hasText()) {
|
if (!QAbstractTableModel::dropMimeData(data, action, row, column, parent) && data->hasText()) {
|
||||||
|
@ -337,14 +362,15 @@ QStringList FieldModel::mimeTypes() const
|
||||||
return QAbstractTableModel::mimeTypes() << QStringLiteral("text/plain");
|
return QAbstractTableModel::mimeTypes() << QStringLiteral("text/plain");
|
||||||
}
|
}
|
||||||
|
|
||||||
QMimeData *FieldModel::mimeData(const QModelIndexList &indexes) const
|
QMimeData *FieldModel::mimeData(const QModelIndexList &indices) const
|
||||||
{
|
{
|
||||||
QMimeData *data = QAbstractTableModel::mimeData(indexes);
|
QMimeData *const data = QAbstractTableModel::mimeData(indices);
|
||||||
if (indexes.isEmpty()) {
|
if (indices.isEmpty()) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
QStringList result;
|
QStringList result;
|
||||||
for (const QModelIndex &index : indexes) {
|
result.reserve(indices.size());
|
||||||
|
for (const QModelIndex &index : indices) {
|
||||||
result << index.data(Qt::EditRole).toString();
|
result << index.data(Qt::EditRole).toString();
|
||||||
}
|
}
|
||||||
data->setText(result.join(QChar('\n')));
|
data->setText(result.join(QChar('\n')));
|
||||||
|
|
|
@ -25,6 +25,7 @@ enum FieldModelRoles {
|
||||||
Key,
|
Key,
|
||||||
Value,
|
Value,
|
||||||
IsPassword,
|
IsPassword,
|
||||||
|
AlwaysActualValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -67,9 +68,10 @@ public:
|
||||||
Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
|
Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
|
||||||
Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
|
Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
|
||||||
|
Q_INVOKABLE bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild);
|
||||||
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
|
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
|
||||||
QStringList mimeTypes() const;
|
QStringList mimeTypes() const;
|
||||||
QMimeData *mimeData(const QModelIndexList &indexes) const;
|
QMimeData *mimeData(const QModelIndexList &indices) const;
|
||||||
Q_INVOKABLE const Io::Field *field(std::size_t row) const;
|
Q_INVOKABLE const Io::Field *field(std::size_t row) const;
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
|
|
|
@ -119,6 +119,64 @@ Kirigami.ScrollablePage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// component representing a field
|
||||||
|
Component {
|
||||||
|
id: fieldsListDelegateComponent
|
||||||
|
RowLayout {
|
||||||
|
id: fieldsListItem
|
||||||
|
|
||||||
|
width: fieldsSheet.width
|
||||||
|
|
||||||
|
Kirigami.ListItemDragHandle {
|
||||||
|
listItem: fieldsListItem
|
||||||
|
listView: fieldsListView
|
||||||
|
onMoveRequested: fieldsListView.model.moveRows(
|
||||||
|
fieldsListView.model.index(-1, 0),
|
||||||
|
oldIndex, 1,
|
||||||
|
fieldsListView.model.index(-1,
|
||||||
|
0), newIndex)
|
||||||
|
}
|
||||||
|
Controls.TextField {
|
||||||
|
text: model.key ? model.key : ""
|
||||||
|
onEditingFinished: fieldsListView.model.setData(
|
||||||
|
fieldsListView.model.index(index, 0),
|
||||||
|
text)
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
Controls.TextField {
|
||||||
|
text: model.actualValue ? model.actualValue : ""
|
||||||
|
echoMode: model.isPassword ? TextInput.PasswordEchoOnEdit : TextInput.Normal
|
||||||
|
onEditingFinished: fieldsListView.model.setData(
|
||||||
|
fieldsListView.model.index(index, 1),
|
||||||
|
text)
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
Controls.Button {
|
||||||
|
flat: true
|
||||||
|
icon.name: model.isPassword ? "password-show-off" : "password-show-on"
|
||||||
|
onClicked: fieldsListView.model.setData(
|
||||||
|
fieldsListView.model.index(index, 0),
|
||||||
|
model.isPassword ? 0 : 1, 0x0100 + 1)
|
||||||
|
Layout.maximumWidth: Kirigami.Units.iconSizes.large
|
||||||
|
Layout.maximumHeight: Kirigami.Units.iconSizes.large
|
||||||
|
}
|
||||||
|
Controls.Button {
|
||||||
|
flat: true
|
||||||
|
icon.name: "edit-delete"
|
||||||
|
onClicked: fieldsListView.model.removeRows(index, 1)
|
||||||
|
Layout.maximumWidth: Kirigami.Units.iconSizes.large
|
||||||
|
Layout.maximumHeight: Kirigami.Units.iconSizes.large
|
||||||
|
}
|
||||||
|
Controls.Button {
|
||||||
|
flat: true
|
||||||
|
icon.name: "list-add"
|
||||||
|
onClicked: fieldsListView.model.insertRows(index + 1, 1)
|
||||||
|
Layout.maximumWidth: Kirigami.Units.iconSizes.large
|
||||||
|
Layout.maximumHeight: Kirigami.Units.iconSizes.large
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// "sheet" to display field model
|
// "sheet" to display field model
|
||||||
Kirigami.OverlaySheet {
|
Kirigami.OverlaySheet {
|
||||||
id: fieldsSheet
|
id: fieldsSheet
|
||||||
|
@ -130,21 +188,16 @@ Kirigami.ScrollablePage {
|
||||||
id: fieldsListView
|
id: fieldsListView
|
||||||
implicitWidth: Kirigami.Units.gridUnit * 30
|
implicitWidth: Kirigami.Units.gridUnit * 30
|
||||||
model: nativeInterface.fieldModel
|
model: nativeInterface.fieldModel
|
||||||
delegate: RowLayout {
|
moveDisplaced: Transition {
|
||||||
Controls.TextField {
|
YAnimator {
|
||||||
text: key ? key : ""
|
duration: Kirigami.Units.longDuration
|
||||||
onEditingFinished: fieldsListView.model.setData(
|
easing.type: Easing.InOutQuad
|
||||||
fieldsListView.model.index(index,
|
|
||||||
0), text)
|
|
||||||
}
|
|
||||||
Controls.TextField {
|
|
||||||
text: value ? value : ""
|
|
||||||
echoMode: isPassword ? TextInput.PasswordEchoOnEdit : TextInput.Normal
|
|
||||||
onEditingFinished: fieldsListView.model.setData(
|
|
||||||
fieldsListView.model.index(index,
|
|
||||||
1), text)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delegate: Kirigami.DelegateRecycler {
|
||||||
|
width: parent ? parent.width : implicitWidth
|
||||||
|
sourceComponent: fieldsListDelegateComponent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,10 +211,8 @@ Kirigami.ScrollablePage {
|
||||||
Kirigami.ListItemDragHandle {
|
Kirigami.ListItemDragHandle {
|
||||||
listItem: listItem
|
listItem: listItem
|
||||||
listView: entriesListView
|
listView: entriesListView
|
||||||
onMoveRequested: {
|
onMoveRequested: entryModel.moveRows(rootIndex, oldIndex,
|
||||||
entryModel.moveRows(rootIndex, oldIndex, 1,
|
1, rootIndex, newIndex)
|
||||||
rootIndex, newIndex)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Kirigami.Icon {
|
Kirigami.Icon {
|
||||||
width: Kirigami.Units.iconSizes.smallMedium
|
width: Kirigami.Units.iconSizes.smallMedium
|
||||||
|
|
Loading…
Reference in New Issue