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::Value, "value" },
|
||||
{ FieldModelRoles::IsPassword, "isPassword" },
|
||||
{ FieldModelRoles::AlwaysActualValue, "actualValue" },
|
||||
};
|
||||
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)
|
||||
? QString::fromStdString((*m_fields)[static_cast<size_t>(index.row())].value())
|
||||
: 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:
|
||||
return (*m_fields)[static_cast<size_t>(index.row())].type() == FieldType::Password;
|
||||
default:;
|
||||
|
@ -169,11 +172,11 @@ bool FieldModel::setData(const QModelIndex &index, const QVariant &value, int ro
|
|||
switch (index.column()) {
|
||||
case 0:
|
||||
m_fields->at(index.row()).setName(value.toString().toStdString());
|
||||
roles << Qt::EditRole << Key;
|
||||
roles << Qt::DisplayRole << Qt::EditRole << Key;
|
||||
break;
|
||||
case 1:
|
||||
m_fields->at(index.row()).setValue(value.toString().toStdString());
|
||||
roles << Qt::EditRole << Value;
|
||||
roles << Qt::DisplayRole << Qt::EditRole << Value << AlwaysActualValue;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
@ -189,15 +192,16 @@ bool FieldModel::setData(const QModelIndex &index, const QVariant &value, int ro
|
|||
}
|
||||
case Key:
|
||||
m_fields->at(index.row()).setName(value.toString().toStdString());
|
||||
roles << Qt::EditRole << Key;
|
||||
roles << Qt::DisplayRole << Qt::EditRole << Key;
|
||||
break;
|
||||
case Value:
|
||||
case AlwaysActualValue:
|
||||
m_fields->at(index.row()).setValue(value.toString().toStdString());
|
||||
roles << Qt::EditRole << Value;
|
||||
roles << Qt::DisplayRole << Qt::EditRole << Value << AlwaysActualValue;
|
||||
break;
|
||||
case IsPassword:
|
||||
m_fields->at(index.row()).setType(value.toBool() ? FieldType::Password : FieldType::Normal);
|
||||
roles << FieldTypeRole << IsPassword;
|
||||
roles << Qt::DisplayRole << FieldTypeRole << IsPassword;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
@ -218,14 +222,14 @@ bool FieldModel::setData(const QModelIndex &index, const QVariant &value, int ro
|
|||
m_fields->emplace_back(m_accountEntry);
|
||||
m_fields->back().setName(value.toString().toStdString());
|
||||
endInsertRows();
|
||||
roles << role;
|
||||
roles << Qt::DisplayRole << Qt::EditRole;
|
||||
break;
|
||||
case 1:
|
||||
beginInsertRows(index.parent(), rowCount(), rowCount());
|
||||
m_fields->emplace_back(m_accountEntry);
|
||||
m_fields->back().setValue(value.toString().toStdString());
|
||||
endInsertRows();
|
||||
roles << role;
|
||||
roles << Qt::DisplayRole << Qt::EditRole;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
@ -237,19 +241,6 @@ bool FieldModel::setData(const QModelIndex &index, const QVariant &value, int ro
|
|||
if (roles.isEmpty()) {
|
||||
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 dataChanged(index, index, roles);
|
||||
return true;
|
||||
|
@ -324,6 +315,40 @@ bool FieldModel::removeRows(int row, int count, const QModelIndex &parent)
|
|||
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)
|
||||
{
|
||||
if (!QAbstractTableModel::dropMimeData(data, action, row, column, parent) && data->hasText()) {
|
||||
|
@ -337,14 +362,15 @@ QStringList FieldModel::mimeTypes() const
|
|||
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);
|
||||
if (indexes.isEmpty()) {
|
||||
QMimeData *const data = QAbstractTableModel::mimeData(indices);
|
||||
if (indices.isEmpty()) {
|
||||
return data;
|
||||
}
|
||||
QStringList result;
|
||||
for (const QModelIndex &index : indexes) {
|
||||
result.reserve(indices.size());
|
||||
for (const QModelIndex &index : indices) {
|
||||
result << index.data(Qt::EditRole).toString();
|
||||
}
|
||||
data->setText(result.join(QChar('\n')));
|
||||
|
|
|
@ -25,6 +25,7 @@ enum FieldModelRoles {
|
|||
Key,
|
||||
Value,
|
||||
IsPassword,
|
||||
AlwaysActualValue,
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -67,9 +68,10 @@ public:
|
|||
Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
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 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);
|
||||
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;
|
||||
|
||||
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
|
||||
Kirigami.OverlaySheet {
|
||||
id: fieldsSheet
|
||||
|
@ -130,21 +188,16 @@ Kirigami.ScrollablePage {
|
|||
id: fieldsListView
|
||||
implicitWidth: Kirigami.Units.gridUnit * 30
|
||||
model: nativeInterface.fieldModel
|
||||
delegate: RowLayout {
|
||||
Controls.TextField {
|
||||
text: key ? key : ""
|
||||
onEditingFinished: fieldsListView.model.setData(
|
||||
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)
|
||||
moveDisplaced: Transition {
|
||||
YAnimator {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
delegate: Kirigami.DelegateRecycler {
|
||||
width: parent ? parent.width : implicitWidth
|
||||
sourceComponent: fieldsListDelegateComponent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,10 +211,8 @@ Kirigami.ScrollablePage {
|
|||
Kirigami.ListItemDragHandle {
|
||||
listItem: listItem
|
||||
listView: entriesListView
|
||||
onMoveRequested: {
|
||||
entryModel.moveRows(rootIndex, oldIndex, 1,
|
||||
rootIndex, newIndex)
|
||||
}
|
||||
onMoveRequested: entryModel.moveRows(rootIndex, oldIndex,
|
||||
1, rootIndex, newIndex)
|
||||
}
|
||||
Kirigami.Icon {
|
||||
width: Kirigami.Units.iconSizes.smallMedium
|
||||
|
|
Loading…
Reference in New Issue