Quick GUI: Allow filtering entries
So far all operations on entries are disabled when in filtered state and switching between normal and filtered view invalidates the stack.
This commit is contained in:
parent
c3775775d1
commit
fa050a422a
|
@ -1,6 +1,8 @@
|
||||||
#include "./entryfiltermodel.h"
|
#include "./entryfiltermodel.h"
|
||||||
#include "./entrymodel.h"
|
#include "./entrymodel.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
namespace QtGui {
|
namespace QtGui {
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -15,9 +17,43 @@ namespace QtGui {
|
||||||
*/
|
*/
|
||||||
EntryFilterModel::EntryFilterModel(QObject *parent)
|
EntryFilterModel::EntryFilterModel(QObject *parent)
|
||||||
: QSortFilterProxyModel(parent)
|
: QSortFilterProxyModel(parent)
|
||||||
|
, m_sourceModel(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EntryFilterModel::isNode(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return m_sourceModel->isNode(mapToSource(parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryFilterModel::setSourceModel(QAbstractItemModel *sourceModel)
|
||||||
|
{
|
||||||
|
if (!sourceModel) {
|
||||||
|
QSortFilterProxyModel::setSourceModel(sourceModel);
|
||||||
|
m_sourceModel = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *const entryModel = qobject_cast<EntryModel *>(sourceModel);
|
||||||
|
assert(entryModel);
|
||||||
|
QSortFilterProxyModel::setSourceModel(sourceModel);
|
||||||
|
m_sourceModel = entryModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryFilterModel::setInsertTypeToNode()
|
||||||
|
{
|
||||||
|
if (m_sourceModel) {
|
||||||
|
m_sourceModel->setInsertTypeToNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryFilterModel::setInsertTypeToAccount()
|
||||||
|
{
|
||||||
|
if (m_sourceModel) {
|
||||||
|
m_sourceModel->setInsertTypeToAccount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool EntryFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
bool EntryFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||||
{
|
{
|
||||||
// just use default implementation
|
// just use default implementation
|
||||||
|
|
|
@ -5,17 +5,26 @@
|
||||||
|
|
||||||
namespace QtGui {
|
namespace QtGui {
|
||||||
|
|
||||||
|
class EntryModel;
|
||||||
|
|
||||||
class EntryFilterModel : public QSortFilterProxyModel {
|
class EntryFilterModel : public QSortFilterProxyModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit EntryFilterModel(QObject *parent = nullptr);
|
explicit EntryFilterModel(QObject *parent = nullptr);
|
||||||
|
void setSourceModel(QAbstractItemModel *sourceModel) override;
|
||||||
|
Q_INVOKABLE bool isNode(const QModelIndex &parent) const;
|
||||||
|
Q_INVOKABLE void setInsertTypeToNode();
|
||||||
|
Q_INVOKABLE void setInsertTypeToAccount();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool hasAcceptedChildren(const QModelIndex &index) const;
|
bool hasAcceptedChildren(const QModelIndex &index) const;
|
||||||
|
|
||||||
|
EntryModel *m_sourceModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QtGui
|
} // namespace QtGui
|
||||||
|
|
||||||
#endif // ENTRYFILTERMODEL_H
|
#endif // ENTRYFILTERMODEL_H
|
||||||
|
|
|
@ -41,7 +41,7 @@ public:
|
||||||
explicit EntryModel(QUndoStack *undoStack, QObject *parent = nullptr);
|
explicit EntryModel(QUndoStack *undoStack, QObject *parent = nullptr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
Io::NodeEntry *rootEntry();
|
Io::NodeEntry *rootEntry();
|
||||||
void setRootEntry(Io::NodeEntry *entry);
|
void setRootEntry(Io::NodeEntry *entry);
|
||||||
Q_INVOKABLE Io::Entry *entry(const QModelIndex &index);
|
Q_INVOKABLE Io::Entry *entry(const QModelIndex &index);
|
||||||
|
@ -49,26 +49,27 @@ public:
|
||||||
Q_INVOKABLE bool insertEntries(int row, const QModelIndex &parent, const QList<Io::Entry *> &entries);
|
Q_INVOKABLE bool insertEntries(int row, const QModelIndex &parent, const QList<Io::Entry *> &entries);
|
||||||
Io::EntryType insertType() const;
|
Io::EntryType insertType() const;
|
||||||
void setInsertType(Io::EntryType type);
|
void setInsertType(Io::EntryType type);
|
||||||
QModelIndex index(int row, int column, const QModelIndex &parent) const;
|
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
|
||||||
QModelIndex index(Io::Entry *entry) const;
|
QModelIndex index(Io::Entry *entry) const;
|
||||||
QModelIndex parent(const QModelIndex &child) const;
|
QModelIndex parent(const QModelIndex &child) const override;
|
||||||
bool hasChildren(const QModelIndex &parent) const;
|
bool hasChildren(const QModelIndex &parent) const override;
|
||||||
Q_INVOKABLE bool isNode(const QModelIndex &parent) const;
|
Q_INVOKABLE bool isNode(const QModelIndex &parent) const;
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
QMap<int, QVariant> itemData(const QModelIndex &index) const;
|
QMap<int, QVariant> itemData(const QModelIndex &index) const override;
|
||||||
bool setData(const QModelIndex &index, const QVariant &value, int role);
|
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||||
bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles);
|
bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) override;
|
||||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||||
Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
|
Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
|
||||||
Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
|
Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
|
||||||
Q_INVOKABLE bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild);
|
Q_INVOKABLE bool moveRows(
|
||||||
QStringList mimeTypes() const;
|
const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override;
|
||||||
QMimeData *mimeData(const QModelIndexList &indexes) const;
|
QStringList mimeTypes() const override;
|
||||||
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
|
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||||
Qt::DropActions supportedDropActions() const;
|
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
|
||||||
|
Qt::DropActions supportedDropActions() const override;
|
||||||
Q_INVOKABLE void setInsertTypeToNode();
|
Q_INVOKABLE void setInsertTypeToNode();
|
||||||
Q_INVOKABLE void setInsertTypeToAccount();
|
Q_INVOKABLE void setInsertTypeToAccount();
|
||||||
|
|
||||||
|
|
|
@ -53,25 +53,26 @@ public:
|
||||||
explicit FieldModel(QUndoStack *undoStack, QObject *parent = nullptr);
|
explicit FieldModel(QUndoStack *undoStack, QObject *parent = nullptr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
Io::AccountEntry *accountEntry();
|
Io::AccountEntry *accountEntry();
|
||||||
const Io::AccountEntry *accountEntry() const;
|
const Io::AccountEntry *accountEntry() const;
|
||||||
void setAccountEntry(Io::AccountEntry *entry);
|
void setAccountEntry(Io::AccountEntry *entry);
|
||||||
std::vector<Io::Field> *fields();
|
std::vector<Io::Field> *fields();
|
||||||
PasswordVisibility passwordVisibility() const;
|
PasswordVisibility passwordVisibility() const;
|
||||||
QVariant data(const QModelIndex &index, int role) const;
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
QMap<int, QVariant> itemData(const QModelIndex &index) const;
|
QMap<int, QVariant> itemData(const QModelIndex &index) const override;
|
||||||
bool setData(const QModelIndex &index, const QVariant &value, int role);
|
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||||
Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
|
Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
|
||||||
Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
|
Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
|
||||||
Q_INVOKABLE bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild);
|
Q_INVOKABLE bool moveRows(
|
||||||
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
|
const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override;
|
||||||
QStringList mimeTypes() const;
|
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
|
||||||
QMimeData *mimeData(const QModelIndexList &indices) const;
|
QStringList mimeTypes() const override;
|
||||||
|
QMimeData *mimeData(const QModelIndexList &indices) const override;
|
||||||
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:
|
||||||
|
|
|
@ -11,18 +11,24 @@ Kirigami.ScrollablePage {
|
||||||
property alias rootIndex: delegateModel.rootIndex
|
property alias rootIndex: delegateModel.rootIndex
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
title: entryModel.data(rootIndex)
|
title: {
|
||||||
|
var currentEntryName = entryModel.data(rootIndex)
|
||||||
|
return currentEntryName ? currentEntryName : ""
|
||||||
|
}
|
||||||
actions {
|
actions {
|
||||||
main: Kirigami.Action {
|
main: Kirigami.Action {
|
||||||
iconName: "list-add"
|
iconName: "list-add"
|
||||||
text: qsTr("Add account")
|
text: qsTr("Add account")
|
||||||
|
visible: !nativeInterface.hasEntryFilter
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: insertEntry("Account")
|
onTriggered: insertEntry("Account")
|
||||||
shortcut: "Ctrl+A"
|
shortcut: "Ctrl+A"
|
||||||
}
|
}
|
||||||
left: Kirigami.Action {
|
left: Kirigami.Action {
|
||||||
iconName: "edit-paste"
|
iconName: "edit-paste"
|
||||||
text: qsTr("Paste account")
|
text: qsTr("Paste account")
|
||||||
enabled: nativeInterface.canPaste
|
visible: !nativeInterface.hasEntryFilter
|
||||||
|
enabled: nativeInterface.canPaste && !nativeInterface.hasEntryFilter
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
var pastedEntries = nativeInterface.pasteEntries(rootIndex)
|
var pastedEntries = nativeInterface.pasteEntries(rootIndex)
|
||||||
if (pastedEntries.length < 1) {
|
if (pastedEntries.length < 1) {
|
||||||
|
@ -38,6 +44,8 @@ Kirigami.ScrollablePage {
|
||||||
right: Kirigami.Action {
|
right: Kirigami.Action {
|
||||||
iconName: "folder-add"
|
iconName: "folder-add"
|
||||||
text: qsTr("Add category")
|
text: qsTr("Add category")
|
||||||
|
visible: !nativeInterface.hasEntryFilter
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: insertEntry("Node")
|
onTriggered: insertEntry("Node")
|
||||||
shortcut: "Ctrl+Shift+A"
|
shortcut: "Ctrl+Shift+A"
|
||||||
}
|
}
|
||||||
|
@ -127,6 +135,7 @@ Kirigami.ScrollablePage {
|
||||||
Kirigami.ListItemDragHandle {
|
Kirigami.ListItemDragHandle {
|
||||||
listItem: listItem
|
listItem: listItem
|
||||||
listView: entriesListView
|
listView: entriesListView
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
// FIXME: not sure why newIndex + 1 is required to be able to move a row at the end
|
// FIXME: not sure why newIndex + 1 is required to be able to move a row at the end
|
||||||
onMoveRequested: entryModel.moveRows(
|
onMoveRequested: entryModel.moveRows(
|
||||||
rootIndex, oldIndex, 1, rootIndex,
|
rootIndex, oldIndex, 1, rootIndex,
|
||||||
|
@ -167,6 +176,7 @@ Kirigami.ScrollablePage {
|
||||||
Controls.MenuItem {
|
Controls.MenuItem {
|
||||||
icon.name: "edit-cut"
|
icon.name: "edit-cut"
|
||||||
text: qsTr("Cut")
|
text: qsTr("Cut")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
nativeInterface.cutEntry(
|
nativeInterface.cutEntry(
|
||||||
entryModel.index(index, 0,
|
entryModel.index(index, 0,
|
||||||
|
@ -177,12 +187,14 @@ Kirigami.ScrollablePage {
|
||||||
Controls.MenuItem {
|
Controls.MenuItem {
|
||||||
icon.name: "edit-delete"
|
icon.name: "edit-delete"
|
||||||
text: qsTr("Delete")
|
text: qsTr("Delete")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: confirmDeletionDialog.confirmDeletion(
|
onTriggered: confirmDeletionDialog.confirmDeletion(
|
||||||
model.name, index)
|
model.name, index)
|
||||||
}
|
}
|
||||||
Controls.MenuItem {
|
Controls.MenuItem {
|
||||||
icon.name: "edit-rename"
|
icon.name: "edit-rename"
|
||||||
text: qsTr("Rename")
|
text: qsTr("Rename")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: renameDialog.renameEntry(model.name,
|
onTriggered: renameDialog.renameEntry(model.name,
|
||||||
index)
|
index)
|
||||||
}
|
}
|
||||||
|
@ -193,6 +205,7 @@ Kirigami.ScrollablePage {
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
iconName: "edit-cut"
|
iconName: "edit-cut"
|
||||||
text: qsTr("Cut")
|
text: qsTr("Cut")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
nativeInterface.cutEntry(entryModel.index(index, 0,
|
nativeInterface.cutEntry(entryModel.index(index, 0,
|
||||||
rootIndex))
|
rootIndex))
|
||||||
|
@ -203,6 +216,7 @@ Kirigami.ScrollablePage {
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
iconName: "edit-delete"
|
iconName: "edit-delete"
|
||||||
text: qsTr("Delete")
|
text: qsTr("Delete")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: confirmDeletionDialog.confirmDeletion(
|
onTriggered: confirmDeletionDialog.confirmDeletion(
|
||||||
model.name, index)
|
model.name, index)
|
||||||
shortcut: StandardKey.Delete
|
shortcut: StandardKey.Delete
|
||||||
|
@ -210,6 +224,7 @@ Kirigami.ScrollablePage {
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
iconName: "edit-rename"
|
iconName: "edit-rename"
|
||||||
text: qsTr("Rename")
|
text: qsTr("Rename")
|
||||||
|
enabled: !nativeInterface.hasEntryFilter
|
||||||
onTriggered: renameDialog.renameEntry(model.name, index)
|
onTriggered: renameDialog.renameEntry(model.name, index)
|
||||||
shortcut: "F2"
|
shortcut: "F2"
|
||||||
}
|
}
|
||||||
|
|
61
qml/main.qml
61
qml/main.qml
|
@ -1,4 +1,5 @@
|
||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
|
import QtQuick.Templates 2.0 as T2
|
||||||
import QtQuick.Controls 2.1 as Controls
|
import QtQuick.Controls 2.1 as Controls
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Dialogs 1.3
|
import QtQuick.Dialogs 1.3
|
||||||
|
@ -15,15 +16,6 @@ Kirigami.ApplicationWindow {
|
||||||
minimumHeight: 0
|
minimumHeight: 0
|
||||||
preferredHeight: Kirigami.Units.gridUnit * 2.3
|
preferredHeight: Kirigami.Units.gridUnit * 2.3
|
||||||
maximumHeight: Kirigami.Units.gridUnit * 3
|
maximumHeight: Kirigami.Units.gridUnit * 3
|
||||||
|
|
||||||
/*
|
|
||||||
Controls.TextField {
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
placeholderText: qsTr("Filter")
|
|
||||||
width: Kirigami.Units.gridUnit * 8
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
globalDrawer: Kirigami.GlobalDrawer {
|
globalDrawer: Kirigami.GlobalDrawer {
|
||||||
id: leftMenu
|
id: leftMenu
|
||||||
|
@ -37,6 +29,35 @@ Kirigami.ApplicationWindow {
|
||||||
topContent: ColumnLayout {
|
topContent: ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredHeight: 4
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: filterTextField.implicitHeight
|
||||||
|
enabled: nativeInterface.fileOpen
|
||||||
|
|
||||||
|
Controls.TextField {
|
||||||
|
id: filterTextField
|
||||||
|
anchors.fill: parent
|
||||||
|
placeholderText: qsTr("Filter")
|
||||||
|
onTextChanged: nativeInterface.entryFilter = text
|
||||||
|
}
|
||||||
|
Kirigami.Icon {
|
||||||
|
source: "edit-clear"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 6
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: Kirigami.Units.iconSizes.small
|
||||||
|
height: Kirigami.Units.iconSizes.small
|
||||||
|
visible: filterTextField.text.length !== 0
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: filterTextField.text = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Controls.MenuSeparator {
|
Controls.MenuSeparator {
|
||||||
padding: 0
|
padding: 0
|
||||||
topPadding: 8
|
topPadding: 8
|
||||||
|
@ -104,6 +125,7 @@ Kirigami.ApplicationWindow {
|
||||||
shortcut: StandardKey.Open
|
shortcut: StandardKey.Open
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
|
id: recentlyOpenedAction
|
||||||
text: qsTr("Recently opened ...")
|
text: qsTr("Recently opened ...")
|
||||||
iconName: "document-open-recent"
|
iconName: "document-open-recent"
|
||||||
children: createRecentlyOpenedActions(
|
children: createRecentlyOpenedActions(
|
||||||
|
@ -210,9 +232,7 @@ Kirigami.ApplicationWindow {
|
||||||
nativeInterface.fileName))
|
nativeInterface.fileName))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var entryModel = nativeInterface.entryModel
|
initStack()
|
||||||
var rootIndex = entryModel.index(0, 0)
|
|
||||||
pushStackEntry(entryModel, rootIndex)
|
|
||||||
showPassiveNotification(qsTr("%1 opened").arg(
|
showPassiveNotification(qsTr("%1 opened").arg(
|
||||||
nativeInterface.fileName))
|
nativeInterface.fileName))
|
||||||
leftMenu.close()
|
leftMenu.close()
|
||||||
|
@ -231,6 +251,11 @@ Kirigami.ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onEntryAboutToBeRemoved: {
|
onEntryAboutToBeRemoved: {
|
||||||
|
// get the filter entry index
|
||||||
|
if (nativeInterface.hasEntryFilter) {
|
||||||
|
removedIndex = nativeInterface.filterEntryIndex(removedIndex)
|
||||||
|
}
|
||||||
|
|
||||||
// remove all possibly open stack pages of the removed entry and its children
|
// remove all possibly open stack pages of the removed entry and its children
|
||||||
for (var i = pageStack.depth - 1; i >= 0; --i) {
|
for (var i = pageStack.depth - 1; i >= 0; --i) {
|
||||||
var stackPage = pageStack.get(i)
|
var stackPage = pageStack.get(i)
|
||||||
|
@ -243,6 +268,12 @@ Kirigami.ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onHasEntryFilterChanged: {
|
||||||
|
if (nativeInterface.fileOpen) {
|
||||||
|
pageStack.clear()
|
||||||
|
initStack()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
@ -285,6 +316,12 @@ Kirigami.ApplicationWindow {
|
||||||
onActivated: leftMenu.visible = !leftMenu.visible
|
onActivated: leftMenu.visible = !leftMenu.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initStack() {
|
||||||
|
var entryModel = nativeInterface.hasEntryFilter ? nativeInterface.entryFilterModel : nativeInterface.entryModel
|
||||||
|
var rootIndex = entryModel.index(0, 0)
|
||||||
|
pushStackEntry(entryModel, rootIndex)
|
||||||
|
}
|
||||||
|
|
||||||
function clearStack() {
|
function clearStack() {
|
||||||
pageStack.pop(lastEntriesPage = root.pageStack.initialPage,
|
pageStack.pop(lastEntriesPage = root.pageStack.initialPage,
|
||||||
Controls.StackView.Immediate)
|
Controls.StackView.Immediate)
|
||||||
|
|
|
@ -309,15 +309,41 @@ void Controller::handleRecentFilesChanged()
|
||||||
m_settings.setValue(QStringLiteral("recententries"), m_recentFiles);
|
m_settings.setValue(QStringLiteral("recententries"), m_recentFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Controller::pasteEntries(const QModelIndex &destinationParent, int row)
|
QStringList Controller::pasteEntries(const QModelIndex &destinationParentMaybeFromFilterModel, int row)
|
||||||
{
|
{
|
||||||
if (m_cutEntries.isEmpty() || !m_entryModel.isNode(destinationParent)) {
|
// skip if no entries have been cut
|
||||||
|
if (m_cutEntries.isEmpty()) {
|
||||||
return QStringList();
|
return QStringList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// determine destinationParent and row in the source model
|
||||||
|
QModelIndex destinationParent;
|
||||||
|
if (destinationParentMaybeFromFilterModel.model() == &m_entryFilterModel) {
|
||||||
|
if (row < 0) {
|
||||||
|
row = m_entryFilterModel.rowCount(destinationParentMaybeFromFilterModel);
|
||||||
|
}
|
||||||
|
const auto destinationIndexInFilter = m_entryFilterModel.index(row, 0, destinationParentMaybeFromFilterModel);
|
||||||
|
if (destinationIndexInFilter.isValid()) {
|
||||||
|
const auto destinationIndex = m_entryFilterModel.mapToSource(destinationIndexInFilter);
|
||||||
|
destinationParent = destinationIndex.parent();
|
||||||
|
row = destinationIndex.row();
|
||||||
|
} else {
|
||||||
|
destinationParent = m_entryFilterModel.mapToSource(destinationParentMaybeFromFilterModel);
|
||||||
|
row = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
destinationParent = destinationParentMaybeFromFilterModel;
|
||||||
|
}
|
||||||
if (row < 0) {
|
if (row < 0) {
|
||||||
row = m_entryModel.rowCount(destinationParent);
|
row = m_entryModel.rowCount(destinationParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip if destination is no node
|
||||||
|
if (!m_entryModel.isNode(destinationParent)) {
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// move the entries
|
||||||
QStringList successfullyMovedEntries;
|
QStringList successfullyMovedEntries;
|
||||||
successfullyMovedEntries.reserve(m_cutEntries.size());
|
successfullyMovedEntries.reserve(m_cutEntries.size());
|
||||||
for (const QPersistentModelIndex &cutIndex : m_cutEntries) {
|
for (const QPersistentModelIndex &cutIndex : m_cutEntries) {
|
||||||
|
@ -401,4 +427,17 @@ void Controller::setUseNativeFileDialog(bool useNativeFileDialog)
|
||||||
m_settings.setValue(QStringLiteral("usenativefiledialog"), m_useNativeFileDialog);
|
m_settings.setValue(QStringLiteral("usenativefiledialog"), m_useNativeFileDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::setEntryFilter(const QString &filter)
|
||||||
|
{
|
||||||
|
const auto previousFilter(m_entryFilterModel.filterRegExp().pattern());
|
||||||
|
if (filter == previousFilter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_entryFilterModel.setFilterRegExp(filter);
|
||||||
|
emit entryFilterChanged(filter);
|
||||||
|
if (previousFilter.isEmpty() != filter.isEmpty()) {
|
||||||
|
emit hasEntryFilterChanged(!filter.isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QtGui
|
} // namespace QtGui
|
||||||
|
|
|
@ -34,6 +34,8 @@ class Controller : public QObject {
|
||||||
Q_PROPERTY(QStringList recentFiles READ recentFiles RESET clearRecentFiles NOTIFY recentFilesChanged)
|
Q_PROPERTY(QStringList recentFiles READ recentFiles RESET clearRecentFiles NOTIFY recentFilesChanged)
|
||||||
Q_PROPERTY(bool useNativeFileDialog READ useNativeFileDialog WRITE setUseNativeFileDialog NOTIFY useNativeFileDialogChanged)
|
Q_PROPERTY(bool useNativeFileDialog READ useNativeFileDialog WRITE setUseNativeFileDialog NOTIFY useNativeFileDialogChanged)
|
||||||
Q_PROPERTY(bool supportsNativeFileDialog READ supportsNativeFileDialog NOTIFY supportsNativeFileDialogChanged)
|
Q_PROPERTY(bool supportsNativeFileDialog READ supportsNativeFileDialog NOTIFY supportsNativeFileDialogChanged)
|
||||||
|
Q_PROPERTY(QString entryFilter READ entryFilter WRITE setEntryFilter NOTIFY entryFilterChanged)
|
||||||
|
Q_PROPERTY(bool hasEntryFilter READ hasEntryFilter NOTIFY hasEntryFilterChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Controller(QSettings &settings, const QString &filePath = QString(), QObject *parent = nullptr);
|
explicit Controller(QSettings &settings, const QString &filePath = QString(), QObject *parent = nullptr);
|
||||||
|
@ -53,12 +55,12 @@ public:
|
||||||
Io::AccountEntry *currentAccount();
|
Io::AccountEntry *currentAccount();
|
||||||
void setCurrentAccount(Io::AccountEntry *entry);
|
void setCurrentAccount(Io::AccountEntry *entry);
|
||||||
QModelIndex currentAccountIndex() const;
|
QModelIndex currentAccountIndex() const;
|
||||||
void setCurrentAccountIndex(const QModelIndex &accountIndex);
|
void setCurrentAccountIndex(const QModelIndex &accountIndexMaybeFromFilterModel);
|
||||||
bool hasCurrentAccount() const;
|
bool hasCurrentAccount() const;
|
||||||
const QList<QPersistentModelIndex> &cutEntries() const;
|
const QList<QPersistentModelIndex> &cutEntries() const;
|
||||||
void setCutEntries(const QList<QPersistentModelIndex> &cutEntries);
|
void setCutEntries(const QList<QPersistentModelIndex> &cutEntries);
|
||||||
QString currentAccountName() const;
|
QString currentAccountName() const;
|
||||||
Q_INVOKABLE void cutEntry(const QModelIndex &entryIndex);
|
Q_INVOKABLE void cutEntry(const QModelIndex &entryIndexMaybeFromFilterModel);
|
||||||
Q_INVOKABLE QStringList pasteEntries(const QModelIndex &destinationParent, int row = -1);
|
Q_INVOKABLE QStringList pasteEntries(const QModelIndex &destinationParent, int row = -1);
|
||||||
Q_INVOKABLE bool copyToClipboard(const QString &text) const;
|
Q_INVOKABLE bool copyToClipboard(const QString &text) const;
|
||||||
bool canPaste() const;
|
bool canPaste() const;
|
||||||
|
@ -67,6 +69,10 @@ public:
|
||||||
bool useNativeFileDialog() const;
|
bool useNativeFileDialog() const;
|
||||||
void setUseNativeFileDialog(bool useNativeFileDialog);
|
void setUseNativeFileDialog(bool useNativeFileDialog);
|
||||||
bool supportsNativeFileDialog() const;
|
bool supportsNativeFileDialog() const;
|
||||||
|
Q_INVOKABLE QModelIndex filterEntryIndex(const QModelIndex &entryIndex) const;
|
||||||
|
QString entryFilter() const;
|
||||||
|
void setEntryFilter(const QString &filter);
|
||||||
|
bool hasEntryFilter() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void init();
|
void init();
|
||||||
|
@ -99,6 +105,8 @@ signals:
|
||||||
void useNativeFileDialogChanged(bool useNativeFileDialog);
|
void useNativeFileDialogChanged(bool useNativeFileDialog);
|
||||||
void supportsNativeFileDialogChanged();
|
void supportsNativeFileDialogChanged();
|
||||||
void entryAboutToBeRemoved(const QModelIndex &removedIndex);
|
void entryAboutToBeRemoved(const QModelIndex &removedIndex);
|
||||||
|
void entryFilterChanged(const QString &newFilter);
|
||||||
|
void hasEntryFilterChanged(bool hasEntryFilter);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleEntriesRemoved(const QModelIndex &parentIndex, int first, int last);
|
void handleEntriesRemoved(const QModelIndex &parentIndex, int first, int last);
|
||||||
|
@ -109,6 +117,7 @@ private:
|
||||||
void updateWindowTitle();
|
void updateWindowTitle();
|
||||||
void setFileOpen(bool fileOpen);
|
void setFileOpen(bool fileOpen);
|
||||||
void emitIoError(const QString &when);
|
void emitIoError(const QString &when);
|
||||||
|
QModelIndex ensureSourceEntryIndex(const QModelIndex &entryIndexMaybeFromFilterModel) const;
|
||||||
|
|
||||||
QSettings &m_settings;
|
QSettings &m_settings;
|
||||||
QString m_filePath;
|
QString m_filePath;
|
||||||
|
@ -127,6 +136,12 @@ private:
|
||||||
bool m_useNativeFileDialog;
|
bool m_useNativeFileDialog;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline QModelIndex Controller::ensureSourceEntryIndex(const QModelIndex &entryIndexMaybeFromFilterModel) const
|
||||||
|
{
|
||||||
|
return entryIndexMaybeFromFilterModel.model() == &m_entryFilterModel ? m_entryFilterModel.mapToSource(entryIndexMaybeFromFilterModel)
|
||||||
|
: entryIndexMaybeFromFilterModel;
|
||||||
|
}
|
||||||
|
|
||||||
inline const QString &Controller::filePath() const
|
inline const QString &Controller::filePath() const
|
||||||
{
|
{
|
||||||
return m_filePath;
|
return m_filePath;
|
||||||
|
@ -193,8 +208,9 @@ inline QModelIndex Controller::currentAccountIndex() const
|
||||||
return m_fieldModel.accountEntry() ? m_entryModel.index(const_cast<Io::AccountEntry *>(m_fieldModel.accountEntry())) : QModelIndex();
|
return m_fieldModel.accountEntry() ? m_entryModel.index(const_cast<Io::AccountEntry *>(m_fieldModel.accountEntry())) : QModelIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Controller::setCurrentAccountIndex(const QModelIndex &accountIndex)
|
inline void Controller::setCurrentAccountIndex(const QModelIndex &accountIndexMaybeFromFilterModel)
|
||||||
{
|
{
|
||||||
|
const auto accountIndex = ensureSourceEntryIndex(accountIndexMaybeFromFilterModel);
|
||||||
m_fieldModel.setAccountEntry(m_entryModel.isNode(accountIndex) ? nullptr : static_cast<Io::AccountEntry *>(m_entryModel.entry(accountIndex)));
|
m_fieldModel.setAccountEntry(m_entryModel.isNode(accountIndex) ? nullptr : static_cast<Io::AccountEntry *>(m_entryModel.entry(accountIndex)));
|
||||||
emit currentAccountChanged();
|
emit currentAccountChanged();
|
||||||
}
|
}
|
||||||
|
@ -221,7 +237,7 @@ inline QString Controller::currentAccountName() const
|
||||||
|
|
||||||
inline void Controller::cutEntry(const QModelIndex &entryIndex)
|
inline void Controller::cutEntry(const QModelIndex &entryIndex)
|
||||||
{
|
{
|
||||||
cutEntriesChanged(m_cutEntries << QPersistentModelIndex(entryIndex));
|
cutEntriesChanged(m_cutEntries << QPersistentModelIndex(ensureSourceEntryIndex(entryIndex)));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Controller::canPaste() const
|
inline bool Controller::canPaste() const
|
||||||
|
@ -248,6 +264,21 @@ inline bool Controller::supportsNativeFileDialog() const
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline QModelIndex Controller::filterEntryIndex(const QModelIndex &entryIndex) const
|
||||||
|
{
|
||||||
|
return m_entryFilterModel.mapFromSource(entryIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString Controller::entryFilter() const
|
||||||
|
{
|
||||||
|
return m_entryFilterModel.filterRegExp().pattern();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool Controller::hasEntryFilter() const
|
||||||
|
{
|
||||||
|
return !m_entryFilterModel.filterRegExp().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QtGui
|
} // namespace QtGui
|
||||||
|
|
||||||
#endif // QT_QUICK_GUI_CONTROLLER_H
|
#endif // QT_QUICK_GUI_CONTROLLER_H
|
||||||
|
|
Loading…
Reference in New Issue