Improve Qt Quick GUI after porting to Qt 6
* Avoid use of deprecated QML features * Fix problems when moving items by making the delegates own items * Improve code in models, especially functions for moving rows * Fix sizing of dialogs * Replace code relying on `bannerClicked` event which has been removed in KF6 * Use the native file dialog if supported (and otherwise not); this should give the desired behavior under each platform out of the box * Update versioning requirements in README
This commit is contained in:
parent
4bf6a91d72
commit
6972256727
|
@ -91,7 +91,7 @@ can be passed to CMake to influence the build.
|
||||||
### Optional dependencies
|
### Optional dependencies
|
||||||
* When building any Qt GUI, the library qtutilities is required.
|
* When building any Qt GUI, the library qtutilities is required.
|
||||||
* When building with Qt Widgets GUI support, the following Qt modules are required (version 5.6 or higher): core gui widgets
|
* When building with Qt Widgets GUI support, the following Qt modules are required (version 5.6 or higher): core gui widgets
|
||||||
* When building with support for the experimental Qt Quick GUI, the following Qt/KDE modules are required (version 5.12 or higher): core gui qml quick quickcontrols2 kirigami
|
* When building with support for the experimental Qt Quick GUI, the following Qt/KDE modules are required (version 6.6 or higher): core gui qml quick quickcontrols2 kirigami
|
||||||
|
|
||||||
To specify the major Qt version to use, set `QT_PACKAGE_PREFIX` (e.g. add `-DQT_PACKAGE_PREFIX:STRING=Qt6`
|
To specify the major Qt version to use, set `QT_PACKAGE_PREFIX` (e.g. add `-DQT_PACKAGE_PREFIX:STRING=Qt6`
|
||||||
to the CMake arguments). There's also `KF_PACKAGE_PREFIX` for KDE dependencies. Note that the Qt Quick GUI
|
to the CMake arguments). There's also `KF_PACKAGE_PREFIX` for KDE dependencies. Note that the Qt Quick GUI
|
||||||
|
|
|
@ -197,7 +197,7 @@ bool FieldModelRemoveRowsCommand::internalUndo()
|
||||||
/*!
|
/*!
|
||||||
* \brief Stores the entry path for the specified \a model and \a index in \a res.
|
* \brief Stores the entry path for the specified \a model and \a index in \a res.
|
||||||
*/
|
*/
|
||||||
void indexToPath(EntryModel *model, const QModelIndex &index, list<string> &res)
|
static void indexToPath(EntryModel *model, const QModelIndex &index, list<string> &res)
|
||||||
{
|
{
|
||||||
res.clear();
|
res.clear();
|
||||||
if (Entry *entry = model->entry(index)) {
|
if (Entry *entry = model->entry(index)) {
|
||||||
|
@ -209,7 +209,7 @@ void indexToPath(EntryModel *model, const QModelIndex &index, list<string> &res)
|
||||||
* \brief Fetches the entry for the specified \a model and \a path.
|
* \brief Fetches the entry for the specified \a model and \a path.
|
||||||
* \remarks The \a path will be modified. To prevent this use entryFromPathCpy().
|
* \remarks The \a path will be modified. To prevent this use entryFromPathCpy().
|
||||||
*/
|
*/
|
||||||
Entry *entryFromPath(EntryModel *model, list<string> &path)
|
static Entry *entryFromPath(EntryModel *model, list<string> &path)
|
||||||
{
|
{
|
||||||
if (NodeEntry *rootEntry = model->rootEntry()) {
|
if (NodeEntry *rootEntry = model->rootEntry()) {
|
||||||
return rootEntry->entryByPath(path);
|
return rootEntry->entryByPath(path);
|
||||||
|
@ -220,7 +220,7 @@ Entry *entryFromPath(EntryModel *model, list<string> &path)
|
||||||
/*!
|
/*!
|
||||||
* \brief Fetches the entry for the specified \a model and \a path.
|
* \brief Fetches the entry for the specified \a model and \a path.
|
||||||
*/
|
*/
|
||||||
Entry *entryFromPathCpy(EntryModel *model, list<string> path)
|
static Entry *entryFromPathCpy(EntryModel *model, list<string> path)
|
||||||
{
|
{
|
||||||
return entryFromPath(model, path);
|
return entryFromPath(model, path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -452,31 +452,39 @@ bool EntryModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int co
|
||||||
if (undoStack()) {
|
if (undoStack()) {
|
||||||
return push(make_unique<EntryModelMoveRowsCommand>(this, sourceParent, sourceRow, count, destinationParent, destinationChild));
|
return push(make_unique<EntryModelMoveRowsCommand>(this, sourceParent, sourceRow, count, destinationParent, destinationChild));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#if CPP_UTILITIES_DEBUG_BUILD
|
||||||
|
std::cout << "sourceRow: " << sourceRow << endl;
|
||||||
|
std::cout << "destinationChild: " << destinationChild << endl;
|
||||||
#endif
|
#endif
|
||||||
// check validation of specified arguments: source and destination parent entries need to be node entries
|
// check validation of specified arguments: source and destination parent entries need to be node entries
|
||||||
if (sourceRow < 0 || count <= 0) {
|
if (sourceRow < 0 || count <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto *const srcParentEntry = entry(sourceParent);
|
const auto *const srcParentEntry = entry(sourceParent);
|
||||||
const auto *const destParentEntry = entry(sourceParent);
|
const auto *const destParentEntry = entry(destinationParent);
|
||||||
if (!srcParentEntry || !destParentEntry || srcParentEntry->type() != EntryType::Node || destParentEntry->type() != EntryType::Node) {
|
if (!srcParentEntry || !destParentEntry || srcParentEntry->type() != EntryType::Node || destParentEntry->type() != EntryType::Node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// determine the source parent entry and dest parent entry as node entries
|
// determine the source parent entry and dest parent entry as node entries
|
||||||
auto *const srcParentNodeEntry = static_cast<NodeEntry *>(sourceParent.internalPointer());
|
auto *const srcParentNodeEntry = static_cast<NodeEntry *>(sourceParent.internalPointer());
|
||||||
auto *const destParentNodeEntry = static_cast<NodeEntry *>(destinationParent.internalPointer());
|
auto *const destParentNodeEntry = static_cast<NodeEntry *>(destinationParent.internalPointer());
|
||||||
#if CPP_UTILITIES_DEBUG_BUILD
|
|
||||||
cout << "destinationChild: " << destinationChild << endl;
|
|
||||||
#endif
|
|
||||||
// source rows must be within the valid range
|
// source rows must be within the valid range
|
||||||
if (static_cast<size_t>(sourceRow + count) > srcParentNodeEntry->children().size()
|
if (static_cast<std::size_t>(sourceRow + count) > srcParentNodeEntry->children().size()) {
|
||||||
// if source and destination parent are the same the destination child mustn't be in the source range
|
|
||||||
|| !(srcParentNodeEntry != destParentNodeEntry || (destinationChild < sourceRow || (sourceRow + count) < destinationChild))) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// if source and destination parent are the same the destination child mustn't be in the source range
|
||||||
|
if (srcParentNodeEntry == destParentNodeEntry) {
|
||||||
|
if (destinationChild == sourceRow) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(destinationChild < sourceRow || (sourceRow + count) < destinationChild)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
// do not move a row to one of its own children! -> check before
|
// do not move a row to one of its own children! -> check before
|
||||||
for (int index = 0; index < count; ++index) {
|
for (int index = 0; index < count; ++index) {
|
||||||
Entry *const toMove = srcParentNodeEntry->children()[static_cast<size_t>(sourceRow + index)];
|
Entry *const toMove = srcParentNodeEntry->children()[static_cast<std::size_t>(sourceRow + index)];
|
||||||
if (toMove->type() != EntryType::Node) {
|
if (toMove->type() != EntryType::Node) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -485,9 +493,11 @@ bool EntryModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// actually perform the move operation
|
// actually perform the move operation
|
||||||
beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild);
|
if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
for (int index = 0; index < count; ++index) {
|
for (int index = 0; index < count; ++index) {
|
||||||
Entry *toMove = srcParentNodeEntry->children()[static_cast<size_t>(sourceRow + index)];
|
Entry *toMove = srcParentNodeEntry->children()[static_cast<std::size_t>(sourceRow + index)];
|
||||||
if (srcParentNodeEntry == destParentNodeEntry && sourceRow < destinationChild) {
|
if (srcParentNodeEntry == destParentNodeEntry && sourceRow < destinationChild) {
|
||||||
toMove->setParent(destParentNodeEntry, destinationChild + index - 1);
|
toMove->setParent(destParentNodeEntry, destinationChild + index - 1);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -233,17 +233,15 @@ bool FieldModel::setData(const QModelIndex &index, const QVariant &value, int ro
|
||||||
switch (index.column()) {
|
switch (index.column()) {
|
||||||
case 0:
|
case 0:
|
||||||
beginInsertRows(index.parent(), rowCount(), rowCount());
|
beginInsertRows(index.parent(), rowCount(), rowCount());
|
||||||
m_fields->emplace_back(m_accountEntry);
|
m_fields->emplace_back(m_accountEntry).setName(value.toString().toStdString());
|
||||||
m_fields->back().setName(value.toString().toStdString());
|
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
roles << Qt::DisplayRole << Qt::EditRole << IsLastRow;
|
roles << Qt::DisplayRole << Qt::EditRole << Key << IsLastRow;
|
||||||
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).setValue(value.toString().toStdString());
|
||||||
m_fields->back().setValue(value.toString().toStdString());
|
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
roles << Qt::DisplayRole << Qt::EditRole << IsLastRow;
|
roles << Qt::DisplayRole << Qt::EditRole << Value << IsLastRow;
|
||||||
break;
|
break;
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
@ -335,25 +333,22 @@ bool FieldModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int co
|
||||||
|
|
||||||
// validate input parameter
|
// validate input parameter
|
||||||
if (sourceParent.isValid() || destinationParent.isValid() || sourceRow < 0 || count <= 0 || destinationChild < 0
|
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()
|
|| static_cast<std::size_t>(sourceRow + count) > m_fields->size() || static_cast<std::size_t>(destinationChild) >= m_fields->size()
|
||||||
|| (destinationChild >= sourceRow && destinationChild < (sourceRow + count))) {
|
|| (destinationChild >= sourceRow && destinationChild < (sourceRow + count))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// begin the move
|
// begin the move
|
||||||
if (destinationChild > sourceRow) {
|
// note: When moving rows down (destinationChild > sourceRow) the third param is still counted in the initial array!
|
||||||
// move rows down: the third param is still counted in the initial array!
|
if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild > sourceRow ? destinationChild + count : destinationChild)) {
|
||||||
beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild + count);
|
return false;
|
||||||
} else {
|
|
||||||
// move rows up
|
|
||||||
beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reserve space for temporary copies (FIXME: possible to avoid this?)
|
// reserve space for temporary copies (FIXME: possible to avoid this?)
|
||||||
m_fields->reserve(m_fields->size() + static_cast<size_t>(count));
|
m_fields->reserve(m_fields->size() + static_cast<std::size_t>(count));
|
||||||
vector<Io::Field> tmp(static_cast<size_t>(count));
|
auto tmp = vector<Io::Field>(static_cast<std::size_t>(count));
|
||||||
// move rows to temporary array
|
// move rows to temporary array
|
||||||
move(m_fields->begin() + sourceRow, m_fields->begin() + sourceRow + count, tmp.begin());
|
std::move(m_fields->begin() + sourceRow, m_fields->begin() + sourceRow + count, tmp.begin());
|
||||||
// erase slots of rows to be moved
|
// erase slots of rows to be moved
|
||||||
m_fields->erase(m_fields->begin() + sourceRow, m_fields->begin() + sourceRow + count);
|
m_fields->erase(m_fields->begin() + sourceRow, m_fields->begin() + sourceRow + count);
|
||||||
// insert rows again at their new position
|
// insert rows again at their new position
|
||||||
|
|
|
@ -7,8 +7,7 @@ BasicDialog {
|
||||||
id: aboutDialog
|
id: aboutDialog
|
||||||
standardButtons: Controls.Dialog.Ok
|
standardButtons: Controls.Dialog.Ok
|
||||||
padding: Kirigami.Units.largeSpacing
|
padding: Kirigami.Units.largeSpacing
|
||||||
|
contentItem: ColumnLayout {
|
||||||
ColumnLayout {
|
|
||||||
width: aboutDialog.availableWidth
|
width: aboutDialog.availableWidth
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
|
|
|
@ -6,9 +6,7 @@ Controls.Dialog {
|
||||||
modal: true
|
modal: true
|
||||||
focus: true
|
focus: true
|
||||||
parent: applicationWindow().overlay
|
parent: applicationWindow().overlay
|
||||||
//anchors.centerIn: parent // enable if requiring at least Qt 5.12 instead of setting x and y manually
|
anchors.centerIn: parent
|
||||||
x: (parent.width - width) / 2
|
|
||||||
y: (parent.height - height) / 2
|
|
||||||
width: Math.min(parent.width, Kirigami.Units.gridUnit * 30)
|
width: Math.min(parent.width, Kirigami.Units.gridUnit * 30)
|
||||||
|
|
||||||
function acceptOnReturn(event) {
|
function acceptOnReturn(event) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import QtQuick 2.4
|
import QtQuick 2.15
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQml.Models 2.2
|
import QtQml.Models 2.2
|
||||||
import QtQuick.Controls 2.4 as Controls
|
import QtQuick.Controls 2.4 as Controls
|
||||||
|
@ -64,8 +64,7 @@ Kirigami.ScrollablePage {
|
||||||
standardButtons: Controls.Dialog.Ok | Controls.Dialog.Cancel
|
standardButtons: Controls.Dialog.Ok | Controls.Dialog.Cancel
|
||||||
title: qsTr("Delete %1?").arg(entryDesc)
|
title: qsTr("Delete %1?").arg(entryDesc)
|
||||||
onAccepted: entryModel.removeRows(this.entryIndex, 1, rootIndex)
|
onAccepted: entryModel.removeRows(this.entryIndex, 1, rootIndex)
|
||||||
|
contentItem: ColumnLayout {
|
||||||
ColumnLayout {
|
|
||||||
Controls.Label {
|
Controls.Label {
|
||||||
text: " "
|
text: " "
|
||||||
}
|
}
|
||||||
|
@ -109,13 +108,12 @@ Kirigami.ScrollablePage {
|
||||||
entryModel.removeRows(this.entryIndex, 1, rootIndex)
|
entryModel.removeRows(this.entryIndex, 1, rootIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
contentItem: ColumnLayout {
|
||||||
ColumnLayout {
|
|
||||||
Controls.TextField {
|
Controls.TextField {
|
||||||
id: entryNameTextField
|
id: entryNameTextField
|
||||||
Layout.preferredWidth: renameDialog.availableWidth
|
Layout.preferredWidth: renameDialog.availableWidth
|
||||||
placeholderText: qsTr("enter new name here")
|
placeholderText: qsTr("enter new name here")
|
||||||
Keys.onPressed: renameDialog.acceptOnReturn(event)
|
Keys.onPressed: (event) => renameDialog.acceptOnReturn(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,115 +136,6 @@ Kirigami.ScrollablePage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// component representing an entry
|
|
||||||
Component {
|
|
||||||
id: listDelegateComponent
|
|
||||||
|
|
||||||
Kirigami.SwipeListItem {
|
|
||||||
id: listItem
|
|
||||||
contentItem: RowLayout {
|
|
||||||
Kirigami.ListItemDragHandle {
|
|
||||||
listItem: listItem
|
|
||||||
listView: entriesListView
|
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
|
||||||
// FIXME: not sure why newIndex + 1 is required to be able to move a row at the end
|
|
||||||
onMoveRequested: entryModel.moveRows(
|
|
||||||
rootIndex, oldIndex, 1, rootIndex,
|
|
||||||
oldIndex < newIndex ? newIndex + 1 : newIndex)
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
Kirigami.Icon {
|
|
||||||
width: Kirigami.Units.iconSizes.smallMedium
|
|
||||||
height: Kirigami.Units.iconSizes.smallMedium
|
|
||||||
Layout.fillHeight: true
|
|
||||||
source: delegateModel.isNode(
|
|
||||||
index) ? "folder-symbolic" : "story-editor"
|
|
||||||
}
|
|
||||||
Controls.Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
text: model.name
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
onClicked: {
|
|
||||||
if (mouse.button === Qt.RightButton) {
|
|
||||||
entryContextMenu.popup()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
delegateModel.handleEntryClicked(index, model.name)
|
|
||||||
}
|
|
||||||
onPressAndHold: entryContextMenu.popup()
|
|
||||||
}
|
|
||||||
Controls.Menu {
|
|
||||||
id: entryContextMenu
|
|
||||||
Controls.MenuItem {
|
|
||||||
icon.name: "edit-cut"
|
|
||||||
text: qsTr("Cut")
|
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
|
||||||
onTriggered: {
|
|
||||||
nativeInterface.cutEntry(
|
|
||||||
entryModel.index(index, 0,
|
|
||||||
rootIndex))
|
|
||||||
showPassiveNotification(qsTr("Cut %1").arg(
|
|
||||||
model.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Controls.MenuItem {
|
|
||||||
icon.name: "edit-delete"
|
|
||||||
text: qsTr("Delete")
|
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
|
||||||
onTriggered: confirmDeletionDialog.confirmDeletion(
|
|
||||||
model.name, index)
|
|
||||||
}
|
|
||||||
Controls.MenuItem {
|
|
||||||
icon.name: "edit-rename"
|
|
||||||
text: qsTr("Rename")
|
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
|
||||||
onTriggered: renameDialog.renameEntry(model.name,
|
|
||||||
index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
actions: [
|
|
||||||
Kirigami.Action {
|
|
||||||
icon.name: "edit-cut"
|
|
||||||
text: qsTr("Cut")
|
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
|
||||||
onTriggered: {
|
|
||||||
nativeInterface.cutEntry(entryModel.index(index, 0,
|
|
||||||
rootIndex))
|
|
||||||
showPassiveNotification(text + " " + model.name)
|
|
||||||
}
|
|
||||||
shortcut: StandardKey.Cut
|
|
||||||
},
|
|
||||||
Kirigami.Action {
|
|
||||||
icon.name: "edit-delete"
|
|
||||||
text: qsTr("Delete")
|
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
|
||||||
onTriggered: confirmDeletionDialog.confirmDeletion(
|
|
||||||
model.name, index)
|
|
||||||
shortcut: StandardKey.Delete
|
|
||||||
},
|
|
||||||
Kirigami.Action {
|
|
||||||
icon.name: "edit-rename"
|
|
||||||
text: qsTr("Rename")
|
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
|
||||||
onTriggered: renameDialog.renameEntry(model.name, index)
|
|
||||||
shortcut: "F2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// list view to display one hierarchy level of entry model
|
// list view to display one hierarchy level of entry model
|
||||||
ListView {
|
ListView {
|
||||||
id: entriesListView
|
id: entriesListView
|
||||||
|
@ -257,9 +146,17 @@ Kirigami.ScrollablePage {
|
||||||
easing.type: Easing.InOutQuad
|
easing.type: Easing.InOutQuad
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
reuseItems: true
|
||||||
model: DelegateModel {
|
model: DelegateModel {
|
||||||
id: delegateModel
|
id: delegateModel
|
||||||
delegate: listDelegateComponent
|
delegate: EntryDelegate {
|
||||||
|
width: entriesListView.width
|
||||||
|
view: entriesListView
|
||||||
|
onMoveRequested:
|
||||||
|
(oldIndex, newIndex) => {
|
||||||
|
entryModel.moveRows(rootIndex, oldIndex, 1, rootIndex, oldIndex < newIndex ? newIndex + 1 : newIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function isNode(rowNumber) {
|
function isNode(rowNumber) {
|
||||||
return entryModel.isNode(entryModel.index(rowNumber, 0,
|
return entryModel.isNode(entryModel.index(rowNumber, 0,
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
import QtQuick 2.4
|
||||||
|
import QtQuick.Layouts 1.2
|
||||||
|
import QtQml.Models 2.2
|
||||||
|
import QtQuick.Controls 2.4 as Controls
|
||||||
|
import org.kde.kirigami 2.5 as Kirigami
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: delegate
|
||||||
|
|
||||||
|
property ListView view
|
||||||
|
implicitHeight : listItem.implicitHeight
|
||||||
|
|
||||||
|
signal moveRequested(int oldIndex, int newIndex)
|
||||||
|
|
||||||
|
Kirigami.SwipeListItem {
|
||||||
|
id: listItem
|
||||||
|
contentItem: RowLayout {
|
||||||
|
Kirigami.ListItemDragHandle {
|
||||||
|
listItem: listItem
|
||||||
|
listView: view
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
|
onMoveRequested:
|
||||||
|
(oldIndex, newIndex) => delegate.moveRequested(oldIndex, newIndex)
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
Kirigami.Icon {
|
||||||
|
width: Kirigami.Units.iconSizes.smallMedium
|
||||||
|
height: Kirigami.Units.iconSizes.smallMedium
|
||||||
|
Layout.fillHeight: true
|
||||||
|
source: delegateModel.isNode(
|
||||||
|
index) ? "folder-symbolic" : "story-editor"
|
||||||
|
}
|
||||||
|
Controls.Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
text: model.name
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: (mouse) => {
|
||||||
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
entryContextMenu.popup()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delegateModel.handleEntryClicked(index, model.name)
|
||||||
|
}
|
||||||
|
onPressAndHold: entryContextMenu.popup()
|
||||||
|
}
|
||||||
|
Controls.Menu {
|
||||||
|
id: entryContextMenu
|
||||||
|
Controls.MenuItem {
|
||||||
|
icon.name: "edit-cut"
|
||||||
|
text: qsTr("Cut")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
|
onTriggered: {
|
||||||
|
nativeInterface.cutEntry(
|
||||||
|
entryModel.index(index, 0,
|
||||||
|
rootIndex))
|
||||||
|
showPassiveNotification(qsTr("Cut %1").arg(
|
||||||
|
model.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Controls.MenuItem {
|
||||||
|
icon.name: "edit-delete"
|
||||||
|
text: qsTr("Delete")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
|
onTriggered: confirmDeletionDialog.confirmDeletion(
|
||||||
|
model.name, index)
|
||||||
|
}
|
||||||
|
Controls.MenuItem {
|
||||||
|
icon.name: "edit-rename"
|
||||||
|
text: qsTr("Rename")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
|
onTriggered: renameDialog.renameEntry(model.name,
|
||||||
|
index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actions: [
|
||||||
|
Kirigami.Action {
|
||||||
|
icon.name: "edit-cut"
|
||||||
|
text: qsTr("Cut")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
|
onTriggered: {
|
||||||
|
nativeInterface.cutEntry(entryModel.index(index, 0,
|
||||||
|
rootIndex))
|
||||||
|
showPassiveNotification(text + " " + model.name)
|
||||||
|
}
|
||||||
|
shortcut: StandardKey.Cut
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
icon.name: "edit-delete"
|
||||||
|
text: qsTr("Delete")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
|
onTriggered: confirmDeletionDialog.confirmDeletion(
|
||||||
|
model.name, index)
|
||||||
|
shortcut: StandardKey.Delete
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
icon.name: "edit-rename"
|
||||||
|
text: qsTr("Rename")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
|
onTriggered: renameDialog.renameEntry(model.name, index)
|
||||||
|
shortcut: "F2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
import QtQuick 2.4
|
||||||
|
import QtQuick.Layouts 1.2
|
||||||
|
import QtQml.Models 2.2
|
||||||
|
import QtQuick.Controls 2.4 as Controls
|
||||||
|
import org.kde.kirigami 2.5 as Kirigami
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: delegate
|
||||||
|
|
||||||
|
property ListView view
|
||||||
|
implicitHeight : listItem.implicitHeight
|
||||||
|
|
||||||
|
signal moveRequested(int oldIndex, int newIndex)
|
||||||
|
|
||||||
|
Kirigami.SwipeListItem {
|
||||||
|
id: listItem
|
||||||
|
visible: !model.isLastRow
|
||||||
|
contentItem: RowLayout {
|
||||||
|
id: fieldRow
|
||||||
|
Kirigami.ListItemDragHandle {
|
||||||
|
listItem: listItem
|
||||||
|
listView: view
|
||||||
|
onMoveRequested: (oldIndex, newIndex) => delegate.moveRequested(oldIndex, newIndex)
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
Controls.Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
text: {
|
||||||
|
let pieces = []
|
||||||
|
if (model.key) {
|
||||||
|
pieces.push(model.key)
|
||||||
|
}
|
||||||
|
if (model.value) {
|
||||||
|
pieces.push(model.value)
|
||||||
|
}
|
||||||
|
return pieces.join(": ")
|
||||||
|
}
|
||||||
|
color: palette.text
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: (mouse) => {
|
||||||
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
return fieldContextMenu.popup()
|
||||||
|
}
|
||||||
|
fieldDialog.init(model, index)
|
||||||
|
fieldDialog.open()
|
||||||
|
}
|
||||||
|
onPressAndHold: fieldContextMenu.popup()
|
||||||
|
}
|
||||||
|
Controls.Menu {
|
||||||
|
id: fieldContextMenu
|
||||||
|
Controls.MenuItem {
|
||||||
|
icon.name: !model.isPassword ? "password-show-off" : "password-show-on"
|
||||||
|
text: model.isPassword ? qsTr("Mark as normal field") : qsTr(
|
||||||
|
"Mark as password field")
|
||||||
|
onClicked: view.model.setData(
|
||||||
|
view.model.index(index,
|
||||||
|
0),
|
||||||
|
model.isPassword ? 0 : 1, 0x0100 + 1)
|
||||||
|
}
|
||||||
|
Controls.MenuItem {
|
||||||
|
icon.name: "edit-copy"
|
||||||
|
text: model.isPassword ? qsTr("Copy password") : qsTr(
|
||||||
|
"Copy value")
|
||||||
|
onClicked: showPassiveNotification(
|
||||||
|
nativeInterface.copyToClipboard(
|
||||||
|
model.actualValue) ? qsTr("Copied") : qsTr(
|
||||||
|
"Unable to access clipboard"))
|
||||||
|
}
|
||||||
|
Controls.MenuItem {
|
||||||
|
icon.name: "edit-delete"
|
||||||
|
text: qsTr("Delete field")
|
||||||
|
onClicked: view.model.removeRows(index, 1)
|
||||||
|
}
|
||||||
|
Controls.MenuItem {
|
||||||
|
icon.name: "list-add"
|
||||||
|
text: qsTr("Insert empty field after this")
|
||||||
|
onClicked: view.model.insertRows(
|
||||||
|
index + 1, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actions: [
|
||||||
|
Kirigami.Action {
|
||||||
|
icon.name: !model.isPassword ? "password-show-off" : "password-show-on"
|
||||||
|
text: model.isPassword ? qsTr(
|
||||||
|
"Mark as normal field") : qsTr(
|
||||||
|
"Mark as password field")
|
||||||
|
onTriggered: view.model.setData(
|
||||||
|
view.model.index(index, 0),
|
||||||
|
model.isPassword ? 0 : 1, 0x0100 + 1)
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
icon.name: "edit-copy"
|
||||||
|
text: model.isPassword ? qsTr("Copy password") : qsTr(
|
||||||
|
"Copy value")
|
||||||
|
onTriggered: showPassiveNotification(
|
||||||
|
nativeInterface.copyToClipboard(
|
||||||
|
model.actualValue) ? qsTr("Copied") : qsTr(
|
||||||
|
"Unable to access clipboard"))
|
||||||
|
shortcut: StandardKey.Cut
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
icon.name: "edit-delete"
|
||||||
|
text: qsTr("Delete field")
|
||||||
|
onTriggered: view.model.removeRows(index, 1)
|
||||||
|
shortcut: StandardKey.Delete
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
icon.name: "list-add"
|
||||||
|
text: qsTr("Insert empty field after this")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
|
onTriggered: view.model.insertRows(index + 1, 1)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import QtQuick 2.4
|
import QtQuick 2.15
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQml.Models 2.2
|
import QtQml.Models 2.2
|
||||||
import QtQuick.Controls 2.4 as Controls
|
import QtQuick.Controls 2.4 as Controls
|
||||||
|
@ -10,6 +10,21 @@ Kirigami.ScrollablePage {
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
title: nativeInterface.currentAccountName
|
title: nativeInterface.currentAccountName
|
||||||
|
actions:[
|
||||||
|
Kirigami.Action {
|
||||||
|
icon.name: "list-add"
|
||||||
|
text: qsTr("Add field")
|
||||||
|
visible: !nativeInterface.hasEntryFilter
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
|
onTriggered: {
|
||||||
|
const delegateModel = fieldsListView.model
|
||||||
|
const row = delegateModel.rowCount() - 1
|
||||||
|
fieldDialog.init(delegateModel, row)
|
||||||
|
fieldDialog.open()
|
||||||
|
}
|
||||||
|
shortcut: "Ctrl+Shift+A"
|
||||||
|
}
|
||||||
|
]
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Kirigami.Theme.backgroundColor
|
color: Kirigami.Theme.backgroundColor
|
||||||
}
|
}
|
||||||
|
@ -32,8 +47,7 @@ Kirigami.ScrollablePage {
|
||||||
fieldsListView.model.setData(column0, isPassword ? 1 : 0,
|
fieldsListView.model.setData(column0, isPassword ? 1 : 0,
|
||||||
0x0100 + 1)
|
0x0100 + 1)
|
||||||
}
|
}
|
||||||
|
contentItem: ColumnLayout {
|
||||||
ColumnLayout {
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
Layout.preferredWidth: fieldDialog.availableWidth
|
Layout.preferredWidth: fieldDialog.availableWidth
|
||||||
columns: 2
|
columns: 2
|
||||||
|
@ -43,7 +57,7 @@ Kirigami.ScrollablePage {
|
||||||
id: fieldNameEdit
|
id: fieldNameEdit
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: fieldDialog.fieldName
|
text: fieldDialog.fieldName
|
||||||
Keys.onPressed: fieldDialog.acceptOnReturn(event)
|
Keys.onPressed: (event) => fieldDialog.acceptOnReturn(event)
|
||||||
}
|
}
|
||||||
Controls.RoundButton {
|
Controls.RoundButton {
|
||||||
flat: true
|
flat: true
|
||||||
|
@ -69,7 +83,7 @@ Kirigami.ScrollablePage {
|
||||||
// fix ugly bullet points under Android
|
// fix ugly bullet points under Android
|
||||||
font.pointSize: hideCharacters ? fieldNameEdit.font.pointSize
|
font.pointSize: hideCharacters ? fieldNameEdit.font.pointSize
|
||||||
* 0.5 : fieldNameEdit.font.pointSize
|
* 0.5 : fieldNameEdit.font.pointSize
|
||||||
Keys.onPressed: fieldDialog.acceptOnReturn(event)
|
Keys.onPressed: (event) => fieldDialog.acceptOnReturn(event)
|
||||||
}
|
}
|
||||||
Controls.RoundButton {
|
Controls.RoundButton {
|
||||||
flat: true
|
flat: true
|
||||||
|
@ -107,166 +121,27 @@ Kirigami.ScrollablePage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// component representing a field
|
|
||||||
Component {
|
|
||||||
id: fieldsListDelegateComponent
|
|
||||||
|
|
||||||
Kirigami.SwipeListItem {
|
|
||||||
id: fieldsListItem
|
|
||||||
contentItem: RowLayout {
|
|
||||||
id: fieldRow
|
|
||||||
readonly property bool isLast: model.isLastRow
|
|
||||||
|
|
||||||
Kirigami.ListItemDragHandle {
|
|
||||||
listItem: fieldsListItem
|
|
||||||
listView: fieldsListView
|
|
||||||
onMoveRequested: fieldsListView.model.moveRows(
|
|
||||||
fieldsListView.model.index(-1, 0),
|
|
||||||
oldIndex, 1,
|
|
||||||
fieldsListView.model.index(-1, 0),
|
|
||||||
newIndex)
|
|
||||||
visible: !fieldRow.isLast
|
|
||||||
}
|
|
||||||
Kirigami.Icon {
|
|
||||||
width: Kirigami.Units.iconSizes.smallMedium
|
|
||||||
height: width
|
|
||||||
source: "list-add"
|
|
||||||
opacity: 0.6
|
|
||||||
visible: fieldRow.isLast
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
fieldDialog.init(model, index)
|
|
||||||
fieldDialog.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
Controls.Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
text: {
|
|
||||||
if (fieldRow.isLast) {
|
|
||||||
return qsTr("click to append new field")
|
|
||||||
}
|
|
||||||
|
|
||||||
var pieces = []
|
|
||||||
if (model.key) {
|
|
||||||
pieces.push(model.key)
|
|
||||||
}
|
|
||||||
if (model.value) {
|
|
||||||
pieces.push(model.value)
|
|
||||||
}
|
|
||||||
return pieces.join(": ")
|
|
||||||
}
|
|
||||||
color: fieldRow.isLast ? "gray" : palette.text
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
onClicked: {
|
|
||||||
if (mouse.button === Qt.RightButton) {
|
|
||||||
if (!fieldRow.isLast) {
|
|
||||||
fieldContextMenu.popup()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fieldDialog.init(model, index)
|
|
||||||
fieldDialog.open()
|
|
||||||
}
|
|
||||||
onPressAndHold: fieldContextMenu.popup()
|
|
||||||
}
|
|
||||||
Controls.Menu {
|
|
||||||
id: fieldContextMenu
|
|
||||||
Controls.MenuItem {
|
|
||||||
icon.name: !model.isPassword ? "password-show-off" : "password-show-on"
|
|
||||||
text: model.isPassword ? qsTr("Mark as normal field") : qsTr(
|
|
||||||
"Mark as password field")
|
|
||||||
onClicked: fieldsListView.model.setData(
|
|
||||||
fieldsListView.model.index(index,
|
|
||||||
0),
|
|
||||||
model.isPassword ? 0 : 1, 0x0100 + 1)
|
|
||||||
}
|
|
||||||
Controls.MenuItem {
|
|
||||||
icon.name: "edit-copy"
|
|
||||||
text: model.isPassword ? qsTr("Copy password") : qsTr(
|
|
||||||
"Copy value")
|
|
||||||
onClicked: showPassiveNotification(
|
|
||||||
nativeInterface.copyToClipboard(
|
|
||||||
model.actualValue) ? qsTr("Copied") : qsTr(
|
|
||||||
"Unable to access clipboard"))
|
|
||||||
}
|
|
||||||
Controls.MenuItem {
|
|
||||||
icon.name: "edit-delete"
|
|
||||||
text: qsTr("Delete field")
|
|
||||||
onClicked: fieldsListView.model.removeRows(index, 1)
|
|
||||||
}
|
|
||||||
Controls.MenuItem {
|
|
||||||
icon.name: "list-add"
|
|
||||||
text: qsTr("Insert empty field after this")
|
|
||||||
onClicked: fieldsListView.model.insertRows(
|
|
||||||
index + 1, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
actions: [
|
|
||||||
Kirigami.Action {
|
|
||||||
icon.name: !model.isPassword ? "password-show-off" : "password-show-on"
|
|
||||||
text: model.isPassword ? qsTr(
|
|
||||||
"Mark as normal field") : qsTr(
|
|
||||||
"Mark as password field")
|
|
||||||
onTriggered: fieldsListView.model.setData(
|
|
||||||
fieldsListView.model.index(index, 0),
|
|
||||||
model.isPassword ? 0 : 1, 0x0100 + 1)
|
|
||||||
visible: !fieldRow.isLast
|
|
||||||
},
|
|
||||||
Kirigami.Action {
|
|
||||||
icon.name: "edit-copy"
|
|
||||||
text: model.isPassword ? qsTr("Copy password") : qsTr(
|
|
||||||
"Copy value")
|
|
||||||
onTriggered: showPassiveNotification(
|
|
||||||
nativeInterface.copyToClipboard(
|
|
||||||
model.actualValue) ? qsTr("Copied") : qsTr(
|
|
||||||
"Unable to access clipboard"))
|
|
||||||
shortcut: StandardKey.Cut
|
|
||||||
visible: !fieldRow.isLast
|
|
||||||
},
|
|
||||||
Kirigami.Action {
|
|
||||||
icon.name: "edit-delete"
|
|
||||||
text: qsTr("Delete field")
|
|
||||||
onTriggered: fieldsListView.model.removeRows(index, 1)
|
|
||||||
shortcut: StandardKey.Delete
|
|
||||||
visible: !fieldRow.isLast
|
|
||||||
},
|
|
||||||
Kirigami.Action {
|
|
||||||
icon.name: "list-add"
|
|
||||||
text: qsTr("Insert empty field after this")
|
|
||||||
enabled: !nativeInterface.hasEntryFilter
|
|
||||||
onTriggered: fieldsListView.model.insertRows(index + 1, 1)
|
|
||||||
visible: !fieldRow.isLast
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// list view to edit the currently selected account
|
// list view to edit the currently selected account
|
||||||
ListView {
|
ListView {
|
||||||
id: fieldsListView
|
id: fieldsListView
|
||||||
implicitWidth: Kirigami.Units.gridUnit * 30
|
implicitWidth: Kirigami.Units.gridUnit * 30
|
||||||
model: nativeInterface.fieldModel
|
model: nativeInterface.fieldModel
|
||||||
|
reuseItems: true
|
||||||
moveDisplaced: Transition {
|
moveDisplaced: Transition {
|
||||||
YAnimator {
|
YAnimator {
|
||||||
duration: Kirigami.Units.longDuration
|
duration: Kirigami.Units.longDuration
|
||||||
easing.type: Easing.InOutQuad
|
easing.type: Easing.InOutQuad
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delegate: fieldsListDelegateComponent
|
delegate: FieldsDelegate {
|
||||||
|
width: fieldsListView.width
|
||||||
|
view: fieldsListView
|
||||||
|
onMoveRequested:
|
||||||
|
(oldIndex, newIndex) => {
|
||||||
|
const model = fieldsListView.model
|
||||||
|
const invalidIndex = model.index(-1, 0)
|
||||||
|
model.moveRows(invalidIndex, oldIndex, 1, invalidIndex, newIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,7 @@ BasicDialog {
|
||||||
qsTr("You aborted. The password has not been altered."))
|
qsTr("You aborted. The password has not been altered."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
contentItem: ColumnLayout {
|
||||||
ColumnLayout {
|
|
||||||
Controls.Label {
|
Controls.Label {
|
||||||
id: instructionLabel
|
id: instructionLabel
|
||||||
Layout.preferredWidth: passwordDialog.availableWidth
|
Layout.preferredWidth: passwordDialog.availableWidth
|
||||||
|
@ -42,19 +41,21 @@ BasicDialog {
|
||||||
id: passwordTextField
|
id: passwordTextField
|
||||||
Layout.preferredWidth: passwordDialog.availableWidth
|
Layout.preferredWidth: passwordDialog.availableWidth
|
||||||
echoMode: showCharactersCheckBox.checked ? TextInput.Normal : TextInput.Password
|
echoMode: showCharactersCheckBox.checked ? TextInput.Normal : TextInput.Password
|
||||||
placeholderText: qsTr("enter password here, leave empty for no encryption")
|
placeholderText: newPassword
|
||||||
|
? qsTr("enter password here, leave empty for no encryption")
|
||||||
|
: qsTr("enter password here")
|
||||||
color: "#101010"
|
color: "#101010"
|
||||||
placeholderTextColor: "#505050"
|
placeholderTextColor: "#505050"
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
border.color: "#5d5e6d"
|
border.color: "#5d5e6d"
|
||||||
}
|
}
|
||||||
Keys.onPressed: passwordDialog.acceptOnReturn(event)
|
Keys.onPressed: (event) => passwordDialog.acceptOnReturn(event)
|
||||||
}
|
}
|
||||||
Controls.TextField {
|
Controls.TextField {
|
||||||
id: repeatPasswordTextField
|
id: repeatPasswordTextField
|
||||||
Layout.preferredWidth: passwordDialog.availableWidth
|
Layout.preferredWidth: passwordDialog.availableWidth
|
||||||
visible: passwordDialog.newPassword
|
visible: passwordDialog.newPassword
|
||||||
&& !showCharactersCheckBox.checked
|
enabled: visible && !showCharactersCheckBox.checked
|
||||||
echoMode: TextInput.Password
|
echoMode: TextInput.Password
|
||||||
placeholderText: qsTr("repeat password")
|
placeholderText: qsTr("repeat password")
|
||||||
color: "#101010"
|
color: "#101010"
|
||||||
|
|
51
qml/main.qml
51
qml/main.qml
|
@ -16,11 +16,6 @@ Kirigami.ApplicationWindow {
|
||||||
|
|
||||||
title: app.applicationName
|
title: app.applicationName
|
||||||
titleIcon: "qrc://icons/hicolor/scalable/apps/passwordmanager.svg"
|
titleIcon: "qrc://icons/hicolor/scalable/apps/passwordmanager.svg"
|
||||||
// FIXME: not sure why this doesn't work anymore
|
|
||||||
//onBannerClicked: () => {
|
|
||||||
// leftMenu.resetMenu()
|
|
||||||
// aboutDialog.open()
|
|
||||||
//}
|
|
||||||
|
|
||||||
visible: true
|
visible: true
|
||||||
resetMenuOnTriggered: false
|
resetMenuOnTriggered: false
|
||||||
|
@ -208,15 +203,20 @@ Kirigami.ApplicationWindow {
|
||||||
icon.name: "document-close"
|
icon.name: "document-close"
|
||||||
shortcut: StandardKey.Close
|
shortcut: StandardKey.Close
|
||||||
onTriggered: nativeInterface.close()
|
onTriggered: nativeInterface.close()
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
separator: true
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: qsTr("About")
|
||||||
|
icon.name: "help-about"
|
||||||
|
shortcut: "Ctrl+?"
|
||||||
|
onTriggered: {
|
||||||
|
leftMenu.resetMenu()
|
||||||
|
aboutDialog.open()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
Controls.Switch {
|
|
||||||
text: qsTr("Use native file dialog")
|
|
||||||
checked: nativeInterface.useNativeFileDialog
|
|
||||||
visible: nativeInterface.supportsNativeFileDialog
|
|
||||||
onCheckedChanged: nativeInterface.useNativeFileDialog = checked
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
contextDrawer: Kirigami.ContextDrawer {
|
contextDrawer: Kirigami.ContextDrawer {
|
||||||
id: contextDrawer
|
id: contextDrawer
|
||||||
|
@ -241,9 +241,9 @@ Kirigami.ApplicationWindow {
|
||||||
id: fileSummaryDialog
|
id: fileSummaryDialog
|
||||||
standardButtons: Controls.Dialog.Ok
|
standardButtons: Controls.Dialog.Ok
|
||||||
title: qsTr("File details")
|
title: qsTr("File details")
|
||||||
|
contentItem: Controls.TextArea {
|
||||||
Controls.Label {
|
|
||||||
id: fileSummaryLabel
|
id: fileSummaryLabel
|
||||||
|
readOnly: true
|
||||||
text: "No file summary available"
|
text: "No file summary available"
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
|
@ -289,8 +289,7 @@ Kirigami.ApplicationWindow {
|
||||||
Controls.DialogButtonBox.buttonRole: Controls.DialogButtonBox.RejectRole
|
Controls.DialogButtonBox.buttonRole: Controls.DialogButtonBox.RejectRole
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
contentItem: ColumnLayout {
|
||||||
ColumnLayout {
|
|
||||||
Controls.TextField {
|
Controls.TextField {
|
||||||
id: filterDialogTextField
|
id: filterDialogTextField
|
||||||
Layout.preferredWidth: filterDialog.availableWidth
|
Layout.preferredWidth: filterDialog.availableWidth
|
||||||
|
@ -301,12 +300,12 @@ Kirigami.ApplicationWindow {
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: nativeInterface
|
target: nativeInterface
|
||||||
onEntryFilterChanged: {
|
function onEntryFilterChanged(newFilter) {
|
||||||
if (filterTextField.text !== newFilter) {
|
if (filterTextField.text !== newFilter) {
|
||||||
filterTextField.text = newFilter
|
filterTextField.text = newFilter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onFileError: {
|
function onFileError(errorMessage, retryAction) {
|
||||||
var retryMethod = null
|
var retryMethod = null
|
||||||
if (retryAction === "load" || retryAction === "save") {
|
if (retryAction === "load" || retryAction === "save") {
|
||||||
retryMethod = retryAction
|
retryMethod = retryAction
|
||||||
|
@ -320,16 +319,16 @@ Kirigami.ApplicationWindow {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onSettingsError: {
|
function onSettingsError(errorMessage) {
|
||||||
showPassiveNotification(errorMessage)
|
showPassiveNotification(errorMessage)
|
||||||
}
|
}
|
||||||
onPasswordRequired: {
|
function onPasswordRequired(filePath) {
|
||||||
enterPasswordDialog.askForExistingPassword(
|
enterPasswordDialog.askForExistingPassword(
|
||||||
qsTr("Password required to open %1").arg(
|
qsTr("Password required to open %1").arg(
|
||||||
nativeInterface.filePath))
|
nativeInterface.filePath))
|
||||||
leftMenu.resetMenu()
|
leftMenu.resetMenu()
|
||||||
}
|
}
|
||||||
onFileOpenChanged: {
|
function onFileOpenChanged(fileOpen) {
|
||||||
clearStack()
|
clearStack()
|
||||||
if (!nativeInterface.fileOpen) {
|
if (!nativeInterface.fileOpen) {
|
||||||
showPassiveNotification(qsTr("%1 closed").arg(
|
showPassiveNotification(qsTr("%1 closed").arg(
|
||||||
|
@ -341,20 +340,20 @@ Kirigami.ApplicationWindow {
|
||||||
nativeInterface.fileName))
|
nativeInterface.fileName))
|
||||||
leftMenu.close()
|
leftMenu.close()
|
||||||
}
|
}
|
||||||
onFileSaved: {
|
function onFileSaved() {
|
||||||
showPassiveNotification(qsTr("%1 saved").arg(
|
showPassiveNotification(qsTr("%1 saved").arg(
|
||||||
nativeInterface.fileName))
|
nativeInterface.fileName))
|
||||||
}
|
}
|
||||||
onNewNotification: {
|
function onNewNotification(message) {
|
||||||
showPassiveNotification(message)
|
showPassiveNotification(message)
|
||||||
}
|
}
|
||||||
onCurrentAccountChanged: {
|
function onCurrentAccountChanged() {
|
||||||
// remove the fields page if the current account has been removed
|
// remove the fields page if the current account has been removed
|
||||||
if (!nativeInterface.hasCurrentAccount) {
|
if (!nativeInterface.hasCurrentAccount) {
|
||||||
pageStack.pop(lastEntriesPage)
|
pageStack.pop(lastEntriesPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onEntryAboutToBeRemoved: {
|
function onEntryAboutToBeRemoved(removedIndex) {
|
||||||
// get the filter entry index
|
// get the filter entry index
|
||||||
if (nativeInterface.hasEntryFilter) {
|
if (nativeInterface.hasEntryFilter) {
|
||||||
removedIndex = nativeInterface.filterEntryIndex(removedIndex)
|
removedIndex = nativeInterface.filterEntryIndex(removedIndex)
|
||||||
|
@ -372,7 +371,7 @@ Kirigami.ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onHasEntryFilterChanged: {
|
function onHasEntryFilterChanged(hasEntryFilter) {
|
||||||
if (nativeInterface.fileOpen) {
|
if (nativeInterface.fileOpen) {
|
||||||
pageStack.clear()
|
pageStack.clear()
|
||||||
initStack()
|
initStack()
|
||||||
|
|
|
@ -43,7 +43,7 @@ Controller::Controller(QSettings &settings, const QString &filePath, QObject *pa
|
||||||
#endif
|
#endif
|
||||||
, m_fileOpen(false)
|
, m_fileOpen(false)
|
||||||
, m_fileModified(false)
|
, m_fileModified(false)
|
||||||
, m_useNativeFileDialog(false)
|
, m_useNativeFileDialog(supportsNativeFileDialog())
|
||||||
, m_filterAsDialog(
|
, m_filterAsDialog(
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
true
|
true
|
||||||
|
|
|
@ -85,8 +85,8 @@ int runQuickGui(int argc, char *argv[], const QtConfigArguments &qtConfigArgs, c
|
||||||
LOAD_QT_TRANSLATIONS;
|
LOAD_QT_TRANSLATIONS;
|
||||||
|
|
||||||
// init Quick GUI
|
// init Quick GUI
|
||||||
auto engine = QQmlApplicationEngine();
|
|
||||||
auto controller = Controller(*settings, file);
|
auto controller = Controller(*settings, file);
|
||||||
|
auto engine = QQmlApplicationEngine();
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
registerControllerForAndroid(&controller);
|
registerControllerForAndroid(&controller);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
<file alias="AboutDialog.qml">../qml/AboutDialog.qml</file>
|
<file alias="AboutDialog.qml">../qml/AboutDialog.qml</file>
|
||||||
<file alias="PasswordDialog.qml">../qml/PasswordDialog.qml</file>
|
<file alias="PasswordDialog.qml">../qml/PasswordDialog.qml</file>
|
||||||
<file alias="EntriesPage.qml">../qml/EntriesPage.qml</file>
|
<file alias="EntriesPage.qml">../qml/EntriesPage.qml</file>
|
||||||
|
<file alias="EntryDelegate.qml">../qml/EntryDelegate.qml</file>
|
||||||
<file alias="FieldsPage.qml">../qml/FieldsPage.qml</file>
|
<file alias="FieldsPage.qml">../qml/FieldsPage.qml</file>
|
||||||
|
<file alias="FieldsDelegate.qml">../qml/FieldsDelegate.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
Loading…
Reference in New Issue