Continue development of Qt Quick GUI
* Add view (written in QML, based on Qt Quick Controls 2 and Kirigami 2) * Add controller (written in C++) * Use existing models (written in C++)
|
@ -61,12 +61,11 @@ set(WIDGETS_UI_FILES
|
|||
)
|
||||
|
||||
set(QML_HEADER_FILES
|
||||
quickgui/applicationinfo.h
|
||||
quickgui/applicationpaths.h
|
||||
quickgui/controller.h
|
||||
quickgui/initiatequick.h
|
||||
)
|
||||
set(QML_SRC_FILES
|
||||
quickgui/applicationinfo.cpp
|
||||
quickgui/controller.cpp
|
||||
quickgui/initiatequick.cpp
|
||||
resources/icons.qrc
|
||||
resources/qml.qrc
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.1 as Controls
|
||||
import org.kde.kirigami 2.4 as Kirigami
|
||||
|
||||
Controls.Dialog {
|
||||
modal: true
|
||||
focus: true
|
||||
parent: applicationWindow().overlay
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
width: Math.min(parent.width, Kirigami.Units.gridUnit * 30)
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQml.Models 2.2
|
||||
import QtQuick.Controls 2.0 as Controls
|
||||
import org.kde.kirigami 2.4 as Kirigami
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
id: page
|
||||
|
||||
property alias model: delegateModel.model
|
||||
property alias rootIndex: delegateModel.rootIndex
|
||||
|
||||
Layout.fillWidth: true
|
||||
title: "?"
|
||||
actions {
|
||||
main: Kirigami.Action {
|
||||
iconName: "list-add"
|
||||
text: qsTr("Add account")
|
||||
onTriggered: {
|
||||
model.setInsertTypeToAccount()
|
||||
model.insertRows(model.rowCount(rootIndex), 1, rootIndex)
|
||||
}
|
||||
}
|
||||
left: Kirigami.Action {
|
||||
iconName: "edit-paste"
|
||||
text: qsTr("Paste account")
|
||||
onTriggered: {
|
||||
|
||||
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
right: Kirigami.Action {
|
||||
iconName: "folder-add"
|
||||
text: qsTr("Add category")
|
||||
onTriggered: {
|
||||
model.setInsertTypeToNode()
|
||||
model.insertRows(model.rowCount(rootIndex), 1, rootIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
onBackRequested: {
|
||||
if (fieldsSheet.sheetOpen) {
|
||||
event.accepted = true
|
||||
fieldsSheet.close()
|
||||
}
|
||||
}
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
}
|
||||
|
||||
// "sheet" to display field model
|
||||
Kirigami.OverlaySheet {
|
||||
id: fieldsSheet
|
||||
parent: applicationWindow().overlay
|
||||
header: Kirigami.Heading {
|
||||
text: qsTr("Edit account ") + nativeInterface.currentAccountName
|
||||
}
|
||||
ListView {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// list view to display one hierarchy level of entry model
|
||||
ListView {
|
||||
id: listView
|
||||
model: DelegateModel {
|
||||
id: delegateModel
|
||||
|
||||
function isNode(rowNumber) {
|
||||
return model.isNode(model.index(rowNumber, 0, rootIndex))
|
||||
}
|
||||
|
||||
function handleEntryClicked(rowNumber, entryName) {
|
||||
var modelIndex = model.index(rowNumber, 0, rootIndex)
|
||||
if (model.isNode(modelIndex)) {
|
||||
root.pushStackEntry(model, modelIndex)
|
||||
} else {
|
||||
nativeInterface.currentAccountIndex = modelIndex
|
||||
fieldsSheet.open()
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Kirigami.SwipeListItem {
|
||||
id: listItem
|
||||
contentItem: RowLayout {
|
||||
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
|
||||
height: Math.max(implicitHeight,
|
||||
Kirigami.Units.iconSizes.smallMedium)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: name
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: delegateModel.handleEntryClicked(index,
|
||||
name)
|
||||
}
|
||||
}
|
||||
}
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
iconName: "edit-cut"
|
||||
text: qsTr("Cut")
|
||||
onTriggered: showPassiveNotification(text + " " + name)
|
||||
},
|
||||
Kirigami.Action {
|
||||
iconName: "edit-delete"
|
||||
text: qsTr("Delete")
|
||||
onTriggered: showPassiveNotification(text + " " + name)
|
||||
},
|
||||
Kirigami.Action {
|
||||
iconName: "edit-rename"
|
||||
text: qsTr("Rename")
|
||||
onTriggered: showPassiveNotification(text + " " + name)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.1 as Controls
|
||||
import QtQuick.Layouts 1.2
|
||||
import org.kde.kirigami 2.4 as Kirigami
|
||||
|
||||
BasicDialog {
|
||||
id: passwordDialog
|
||||
property alias instruction: instructionLabel.text
|
||||
property alias password: passwordTextField.text
|
||||
property bool newPassword: false
|
||||
readonly property bool canAccept: !newPassword
|
||||
|| showCharactersCheckBox.checked
|
||||
|| passwordTextField.text === repeatPasswordTextField.text
|
||||
|
||||
standardButtons: canAccept ? Controls.Dialog.Ok
|
||||
| Controls.Dialog.Cancel : Controls.Dialog.Cancel
|
||||
title: qsTr("Enter password")
|
||||
|
||||
ColumnLayout {
|
||||
Controls.Label {
|
||||
id: instructionLabel
|
||||
Layout.preferredWidth: passwordDialog.availableWidth
|
||||
wrapMode: Controls.Label.Wrap
|
||||
}
|
||||
|
||||
Controls.TextField {
|
||||
id: passwordTextField
|
||||
Layout.preferredWidth: passwordDialog.availableWidth
|
||||
echoMode: showCharactersCheckBox.checked ? TextInput.Normal : TextInput.Password
|
||||
placeholderText: qsTr("enter password here, leave empty for no encryption")
|
||||
background: Rectangle {
|
||||
border.color: "#5d5e6d"
|
||||
}
|
||||
}
|
||||
Controls.TextField {
|
||||
id: repeatPasswordTextField
|
||||
Layout.preferredWidth: passwordDialog.availableWidth
|
||||
visible: passwordDialog.newPassword
|
||||
&& !showCharactersCheckBox.checked
|
||||
echoMode: TextInput.Password
|
||||
placeholderText: qsTr("repeat password")
|
||||
background: Rectangle {
|
||||
border.color: passwordDialog.canAccept ? "#089900" : "#ff0000"
|
||||
}
|
||||
}
|
||||
Controls.CheckBox {
|
||||
id: showCharactersCheckBox
|
||||
text: qsTr("Show characters")
|
||||
checked: false
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
nativeInterface.password = password
|
||||
if (newPassword) {
|
||||
showPassiveNotification(
|
||||
qsTr("The new password will be used when saving next time."))
|
||||
} else {
|
||||
nativeInterface.load()
|
||||
}
|
||||
}
|
||||
|
||||
onRejected: {
|
||||
if (newPassword) {
|
||||
showPassiveNotification(
|
||||
qsTr("You aborted. The password has not been altered."))
|
||||
}
|
||||
}
|
||||
|
||||
function clear() {
|
||||
passwordTextField.text = ""
|
||||
repeatPasswordTextField.text = ""
|
||||
}
|
||||
|
||||
function askForPassword(instruction, newPassword) {
|
||||
this.newPassword = newPassword
|
||||
this.instruction = instruction
|
||||
this.clear()
|
||||
this.open()
|
||||
}
|
||||
|
||||
function askForExistingPassword(instruction) {
|
||||
this.askForPassword(instruction, false)
|
||||
}
|
||||
|
||||
function askForNewPassword(instruction) {
|
||||
this.askForPassword(instruction, true)
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 190 KiB |
296
qml/main.qml
|
@ -1,176 +1,154 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
import martchus.passwordmanager 2.0
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.1 as Controls
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Dialogs 1.3
|
||||
import org.kde.kirigami 2.4 as Kirigami
|
||||
|
||||
import "./pages" as Pages
|
||||
import "./touch" as Touch
|
||||
|
||||
ApplicationWindow {
|
||||
Kirigami.ApplicationWindow {
|
||||
id: root
|
||||
width: 700
|
||||
height: 800
|
||||
title: qsTr("Password Manager")
|
||||
visible: true
|
||||
|
||||
property string statusBarMessage
|
||||
|
||||
property Component startPage: Pages.StartPage {
|
||||
onUpdateStatusBar: statusBarMessage = message
|
||||
onNextPage: if (!isLocked) {
|
||||
pageView.push(accountsPage)
|
||||
clearSearchBox()
|
||||
}
|
||||
function clearStack() {
|
||||
pageStack.pop(root.pageStack.initialPage, Controls.StackView.Immediate)
|
||||
}
|
||||
property Component accountsPage: Pages.AccountsPage {
|
||||
onUpdateStatusBar: statusBarMessage = message
|
||||
onNextPage: if (!isLocked) {
|
||||
pageView.push(fieldsPage)
|
||||
}
|
||||
onPreviousPage: {
|
||||
pageView.pop()
|
||||
|
||||
function pushStackEntry(entryModel, rootIndex) {
|
||||
var title = entryModel.data(rootIndex)
|
||||
var entriesComponent = Qt.createComponent("EntriesPage.qml")
|
||||
if (entriesComponent.status !== Component.Ready) {
|
||||
var errorMessage = "Unable to load EntriesPage.qml: " + entriesComponent.errorString()
|
||||
showPassiveNotification(errorMessage)
|
||||
console.error(errorMessage)
|
||||
return
|
||||
}
|
||||
var entriesPage = entriesComponent.createObject(root, {
|
||||
model: entryModel,
|
||||
rootIndex: rootIndex,
|
||||
title: title
|
||||
})
|
||||
pageStack.push(entriesPage)
|
||||
}
|
||||
property Component fieldsPage: Pages.FieldsPage {
|
||||
onUpdateStatusBar: statusBarMessage = message
|
||||
onNextPage: if (!isLocked) {
|
||||
pageView.pop()
|
||||
}
|
||||
onPreviousPage: {
|
||||
pageView.pop()
|
||||
|
||||
PasswordDialog {
|
||||
id: enterPasswordDialog
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
title: selectExisting ? qsTr("Select an existing file") : qsTr(
|
||||
"Select path for new file")
|
||||
onAccepted: {
|
||||
if (fileUrls.length < 1) {
|
||||
return
|
||||
}
|
||||
nativeInterface.filePath = fileUrls[0]
|
||||
if (selectExisting) {
|
||||
nativeInterface.load()
|
||||
} else {
|
||||
nativeInterface.create()
|
||||
}
|
||||
}
|
||||
onRejected: {
|
||||
showPassiveNotification("Canceled file selection")
|
||||
}
|
||||
|
||||
function openExisting() {
|
||||
this.selectExisting = true
|
||||
this.open()
|
||||
}
|
||||
function createNew() {
|
||||
this.selectExisting = false
|
||||
this.open()
|
||||
}
|
||||
}
|
||||
|
||||
StackView {
|
||||
id: pageView
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
Keys.onReleased: {
|
||||
if (event.key === Qt.Key_Back ||
|
||||
(event.key === Qt.Key_Left && (event.modifiers & Qt.AltModifier))) {
|
||||
if (pageView.depth > 1) {
|
||||
event.accepted = true
|
||||
if (!currentItem.isLocked)
|
||||
currentItem.previousPage()
|
||||
} else {
|
||||
if (!currentItem.hasNoSearchText) {
|
||||
event.accepted = true
|
||||
currentItem.clearSearchBox()
|
||||
Connections {
|
||||
target: nativeInterface
|
||||
onFileError: {
|
||||
showPassiveNotification(errorMessage)
|
||||
}
|
||||
onPasswordRequired: {
|
||||
enterPasswordDialog.askForExistingPassword(
|
||||
qsTr("Password required to open ") + filePath)
|
||||
leftMenu.resetMenu()
|
||||
}
|
||||
onFileOpenChanged: {
|
||||
clearStack()
|
||||
if (!fileOpen) {
|
||||
return
|
||||
}
|
||||
var entryModel = nativeInterface.entryModel
|
||||
var rootIndex = entryModel.index(0, 0)
|
||||
pushStackEntry(entryModel, rootIndex)
|
||||
}
|
||||
}
|
||||
|
||||
header: Kirigami.ApplicationHeader {
|
||||
}
|
||||
globalDrawer: Kirigami.GlobalDrawer {
|
||||
id: leftMenu
|
||||
title: qsTr("Password manager")
|
||||
titleIcon: "passwordmanager"
|
||||
bannerImageSource: "banner.png"
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
text: qsTr("Create new file")
|
||||
iconName: "document-new"
|
||||
onTriggered: fileDialog.createNew()
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: qsTr("Open existing file")
|
||||
iconName: "document-open"
|
||||
onTriggered: fileDialog.openExisting()
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: "Save modifications"
|
||||
enabled: nativeInterface.fileOpen
|
||||
iconName: "document-save"
|
||||
onTriggered: nativeInterface.save()
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: "Change password"
|
||||
enabled: nativeInterface.fileOpen
|
||||
iconName: "dialog-password"
|
||||
onTriggered: enterPasswordDialog.askForNewPassword(
|
||||
"Change password for " + nativeInterface.filePath)
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: "Close file"
|
||||
enabled: nativeInterface.fileOpen
|
||||
iconName: "document-close"
|
||||
onTriggered: nativeInterface.close()
|
||||
}
|
||||
]
|
||||
}
|
||||
contextDrawer: Kirigami.ContextDrawer {
|
||||
id: contextDrawer
|
||||
}
|
||||
pageStack.initialPage: mainPageComponent
|
||||
|
||||
// main app content
|
||||
Component {
|
||||
id: mainPageComponent
|
||||
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
|
||||
Controls.Label {
|
||||
text: {
|
||||
if (nativeInterface.fileOpen) {
|
||||
return qsTr("The file %1 has been opened.").arg(
|
||||
nativeInterface.filePath)
|
||||
} else {
|
||||
return qsTr("No file has been opened.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
initialItem: startPage
|
||||
delegate: StackViewDelegate {
|
||||
pushTransition: StackViewTransition {
|
||||
function transitionFinished(properties) {
|
||||
properties.exitItem.opacity = 1
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: enterItem
|
||||
property: "x"
|
||||
from: target.width
|
||||
to: 0
|
||||
duration: 500
|
||||
easing.type: Easing.OutSine
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: exitItem
|
||||
property: "x"
|
||||
from: 0
|
||||
to: -target.width
|
||||
duration: 500
|
||||
easing.type: Easing.OutSine
|
||||
}
|
||||
}
|
||||
popTransition: StackViewTransition {
|
||||
function transitionFinished(properties)
|
||||
{
|
||||
properties.exitItem.opacity = 1
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: enterItem
|
||||
property: "x"
|
||||
from: -target.width
|
||||
to: 0
|
||||
duration: 500
|
||||
easing.type: Easing.OutSine
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: exitItem
|
||||
property: "x"
|
||||
from: 0
|
||||
to: target.width
|
||||
duration: 500
|
||||
easing.type: Easing.OutSine
|
||||
}
|
||||
}
|
||||
property Component replaceTransition: pushTransition
|
||||
}
|
||||
}
|
||||
statusBar: StatusBar {
|
||||
id: statusbar
|
||||
width: parent.width
|
||||
opacity: label.text !== "" ? 1 : 0
|
||||
property real statusBarHeight: 65 * ApplicationInfo.ratio
|
||||
height: label.text !== "" ? statusBarHeight : 0
|
||||
Behavior on height { NumberAnimation {easing.type: Easing.OutSine}}
|
||||
Behavior on opacity { NumberAnimation {}}
|
||||
style: StatusBarStyle {
|
||||
padding { left: 0; right: 0 ; top: 0 ; bottom: 0}
|
||||
property Component background: Rectangle {
|
||||
implicitHeight: 65 * ApplicationInfo.ratio
|
||||
implicitWidth: root.width
|
||||
color: ApplicationInfo.colors.smokeGray
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Qt.darker(parent.color, 1.5)
|
||||
}
|
||||
Rectangle {
|
||||
y: 1
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
}
|
||||
Touch.TouchLabel {
|
||||
id: label
|
||||
y: 32 * ApplicationInfo.ratio - height / 2
|
||||
width: parent.width // The text will only wrap if an explicit width has been set
|
||||
text: statusBarMessage
|
||||
textFormat: Text.RichText
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
wrapMode: Text.Wrap
|
||||
pixelSize: 18
|
||||
letterSpacing: -0.15
|
||||
color: ApplicationInfo.colors.mediumGray
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
function decreaseFontSizeOnNarrowScreen() {
|
||||
if (label.implicitHeight > statusbar.statusBarHeight) {
|
||||
pixelSize = Math.floor(pixelSize * statusbar.statusBarHeight / label.implicitHeight)
|
||||
}
|
||||
}
|
||||
onTextChanged: {
|
||||
if (text === "") {
|
||||
pixelSize = 18
|
||||
} else {
|
||||
decreaseFontSizeOnNarrowScreen()
|
||||
}
|
||||
}
|
||||
onWidthChanged: decreaseFontSizeOnNarrowScreen()
|
||||
}
|
||||
}
|
||||
|
||||
menuBar: MenuBar {
|
||||
Menu {
|
||||
title: qsTr("Application")
|
||||
MenuItem {
|
||||
text: qsTr("Exit")
|
||||
onTriggered: Qt.quit();
|
||||
}
|
||||
Component.onCompleted: {
|
||||
// load file if one has been specified via CLI argument
|
||||
if (nativeInterface.filePath.length) {
|
||||
nativeInterface.load()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
BasicPage {
|
||||
id: accountsPage
|
||||
title1: qsTr("Accounts")
|
||||
|
||||
pageComponent: Item {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
import "../touch" as Touch
|
||||
|
||||
Item {
|
||||
id: page
|
||||
signal updateStatusBar(string message)
|
||||
signal nextPage
|
||||
signal previousPage
|
||||
signal clearSearchBox
|
||||
|
||||
property Component pageComponent
|
||||
property bool isLocked: Stack.status !== Stack.Active
|
||||
property string title1
|
||||
property string title2
|
||||
property string title3
|
||||
|
||||
property alias searchText: searchField.text
|
||||
property alias hasNoSearchText: searchField.isEmpty
|
||||
signal searchBoxReturn
|
||||
property string statusBarMessageDefault
|
||||
|
||||
property alias topRect: topRect
|
||||
property color topRectColor: ApplicationInfo.colors.yetAnotherBlue
|
||||
|
||||
property bool searchFieldVisisble: false
|
||||
|
||||
Binding {
|
||||
target: ApplicationInfo
|
||||
property: "isPortraitMode"
|
||||
value: page.height > page.width
|
||||
when: !ApplicationInfo.isMobile
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: ApplicationInfo
|
||||
property: "applicationWidth"
|
||||
value: page.width
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: topRect
|
||||
z: 2 // so flickable doesn't draw on top
|
||||
anchors.top: parent.top
|
||||
height: 80 * ApplicationInfo.ratio
|
||||
width: parent.width
|
||||
color: page.Stack.index !== 0 && mouseBack.pressed ?
|
||||
Qt.lighter(topRectColor, 1.2) : topRectColor
|
||||
Rectangle {
|
||||
color: Qt.lighter(parent.color, 1.2)
|
||||
height: 1
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 1
|
||||
width: parent.width
|
||||
}
|
||||
Rectangle {
|
||||
z: 2 // so flickable doesn't draw on top
|
||||
height: 1
|
||||
width: parent.width
|
||||
color: Qt.darker(ApplicationInfo.colors.blue, 1.6)
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: titleRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Separator {
|
||||
Layout.fillWidth: false
|
||||
Layout.preferredWidth: ApplicationInfo.hMargin
|
||||
}
|
||||
Image {
|
||||
source: ApplicationInfo.imagePath("backarrow.png")
|
||||
Layout.preferredWidth: 22 * ApplicationInfo.ratio
|
||||
Layout.preferredHeight: 35 * ApplicationInfo.ratio
|
||||
visible: page.Stack.index > 0
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("Back")
|
||||
function accessiblePressAction () { backPressed() }
|
||||
}
|
||||
Rectangle {
|
||||
opacity: 0
|
||||
Layout.preferredWidth: 20 * ApplicationInfo.ratio
|
||||
Layout.fillHeight: true
|
||||
visible: page.Stack.index > 0
|
||||
}
|
||||
Touch.TouchLabel {
|
||||
id: t1
|
||||
text: title1 + " "
|
||||
color: ApplicationInfo.colors.white
|
||||
pixelSize: 30
|
||||
font.weight: Font.Bold
|
||||
Layout.maximumWidth: ApplicationInfo.applicationWidth - t3.implicitWidth - 2 * ApplicationInfo.hMargin - 5 * ApplicationInfo.ratio - 42 * ApplicationInfo.ratio
|
||||
Layout.alignment: Qt.AlignBaseline
|
||||
}
|
||||
Touch.TouchLabel {
|
||||
text: "- " + title2
|
||||
color: ApplicationInfo.colors.white
|
||||
visible: title2 !== ""
|
||||
pixelSize: 22
|
||||
letterSpacing: -0.15
|
||||
Layout.alignment: Qt.AlignBaseline
|
||||
Layout.maximumWidth: freeSpace > implicitWidth ? freeSpace : 0
|
||||
property real freeSpace: ApplicationInfo.applicationWidth - t1.width - t3.implicitWidth - 2 * ApplicationInfo.hMargin - 5 * ApplicationInfo.ratio - 42 * ApplicationInfo.ratio
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
height: 0
|
||||
}
|
||||
Touch.TouchLabel {
|
||||
id: t3
|
||||
text: title3
|
||||
color: ApplicationInfo.colors.white
|
||||
visible: title3 !== ""
|
||||
pixelSize: 22
|
||||
letterSpacing: -0.15
|
||||
Layout.alignment: Qt.AlignBaseline
|
||||
}
|
||||
Separator {
|
||||
Layout.fillWidth: false
|
||||
Layout.preferredWidth: ApplicationInfo.hMargin
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 5
|
||||
anchors.top: parent.bottom
|
||||
gradient: Gradient {
|
||||
GradientStop {position: 0 ; color: "#40000000"}
|
||||
GradientStop {position: 1 ; color: "#00000000"}
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
id: mouseBack
|
||||
anchors.fill: parent
|
||||
onClicked: backPressed()
|
||||
}
|
||||
}
|
||||
|
||||
function backPressed() {
|
||||
if (!isLocked) page.previousPage()
|
||||
}
|
||||
|
||||
Touch.TouchTextField {
|
||||
id: searchField
|
||||
z: 2
|
||||
visible: searchFieldVisisble
|
||||
anchors.right: topRect.right
|
||||
anchors.top: topRect.top
|
||||
anchors.bottom: topRect.bottom
|
||||
width: ApplicationInfo.isPortraitMode ?
|
||||
parent.width - t1.implicitWidth - ApplicationInfo.ratio * 50 :
|
||||
parent.width/2.5
|
||||
anchors.leftMargin: topRect.width/2
|
||||
anchors.rightMargin: 20 * ApplicationInfo.ratio
|
||||
anchors.margins: 12 * ApplicationInfo.ratio
|
||||
|
||||
placeholderText: qsTr("filter")
|
||||
Layout.fillWidth: true
|
||||
Keys.onReturnPressed: page.searchBoxReturn()
|
||||
Keys.onEnterPressed: page.searchBoxReturn()
|
||||
onClearButtonClicked: page.clearSearchBox()
|
||||
}
|
||||
|
||||
Loader {
|
||||
sourceComponent: pageComponent
|
||||
anchors.top: topRect.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
Rectangle {
|
||||
z: -1
|
||||
anchors.fill: parent
|
||||
color: ApplicationInfo.colors.white
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
BasicPage {
|
||||
id : fieldsPage
|
||||
title1: ApplicationInfo.currentAccountName
|
||||
title3: qsTr("Fields")
|
||||
|
||||
pageComponent: Item {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Layouts 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
Item {
|
||||
implicitHeight: ApplicationInfo.hMargin
|
||||
implicitWidth: ApplicationInfo.hMargin
|
||||
Layout.minimumHeight: 0
|
||||
Layout.minimumWidth: 0
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
import QtQuick.Dialogs 1.2
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
import "../touch" as Touch
|
||||
|
||||
BasicPage {
|
||||
id: startPage
|
||||
title1: qsTr("Password Manager")
|
||||
|
||||
searchFieldVisisble: true
|
||||
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
title: qsTr("Select a password file")
|
||||
selectExisting: true
|
||||
onAccepted: {
|
||||
ApplicationInfo.currentFile = fileUrl
|
||||
nextPage()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.top: topRect.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 5
|
||||
|
||||
Touch.TouchButton {
|
||||
text: qsTr("Create a new password file")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Touch.TouchButton {
|
||||
text: qsTr("Open an existing password file")
|
||||
onClicked: {
|
||||
ApplicationInfo.currentFile = "./test"
|
||||
nextPage() // fileDialog.visible = true
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Touch.TouchLabel {
|
||||
text: qsTr("Recently opened files")
|
||||
}
|
||||
|
||||
Touch.TouchLabel {
|
||||
text: qsTr("Test 1")
|
||||
//Layout.fillWidth: true
|
||||
color: ApplicationInfo.colors.lightGray
|
||||
}
|
||||
Touch.TouchLabel {
|
||||
text: qsTr("Test 2")
|
||||
//Layout.fillWidth: true
|
||||
color: ApplicationInfo.colors.lightGray
|
||||
}
|
||||
Touch.TouchLabel {
|
||||
text: qsTr("Test 3")
|
||||
//Layout.fillWidth: true
|
||||
color: ApplicationInfo.colors.lightGray
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Layouts 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
Rectangle {
|
||||
id: rect
|
||||
height: ApplicationInfo.constants.rowDelegateHeight
|
||||
width: parent.width
|
||||
signal clicked
|
||||
signal deleteEntry
|
||||
|
||||
color: mouseNext.pressed ? ApplicationInfo.colors.smokeGray : ApplicationInfo.colors.white
|
||||
|
||||
GridLayout {
|
||||
id: _grid
|
||||
anchors.fill: parent
|
||||
flow: Qt.LeftToRight
|
||||
rowSpacing: 4 * ApplicationInfo.ratio
|
||||
columnSpacing: 0
|
||||
columns: 2
|
||||
Rectangle {
|
||||
Layout.preferredWidth: ApplicationInfo.hMargin
|
||||
Layout.fillHeight: true
|
||||
opacity: 0
|
||||
}
|
||||
Loader {
|
||||
// sourceComponent:
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Rectangle {
|
||||
id: separator
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.columnSpan: 2
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
z: 1
|
||||
height: 1
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
color: ApplicationInfo.colors.paleGray
|
||||
}
|
||||
MouseArea {
|
||||
id: mouseNext
|
||||
anchors.left: parent.left
|
||||
width: parent.width - 80 * ApplicationInfo.ratio - ApplicationInfo.hMargin
|
||||
height: parent.height
|
||||
onClicked: rect.clicked()
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Layouts 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
Button {
|
||||
id: button
|
||||
property int pixelSize: 34
|
||||
property real letterSpacing: -0.25
|
||||
property string text: ""
|
||||
|
||||
style: ButtonStyle {
|
||||
label: Label {
|
||||
id: label
|
||||
font.family: "Open Sans"
|
||||
font.pixelSize: pixelSize * ApplicationInfo.ratio * 1.1 // increasing fonts
|
||||
font.letterSpacing: letterSpacing * ApplicationInfo.ratio
|
||||
color: ApplicationInfo.colors.doubleDarkGray
|
||||
verticalAlignment: Text.AlignBottom
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideRight
|
||||
linkColor: ApplicationInfo.colors.blue
|
||||
renderType: ApplicationInfo.isMobile ? Text.QtRendering : Text.NativeRendering
|
||||
text: button.text
|
||||
|
||||
Text {
|
||||
id: text
|
||||
visible: false
|
||||
font.family: label.font.family
|
||||
font.pixelSize: label.font.pixelSize
|
||||
font.letterSpacing: label.font.letterSpacing
|
||||
wrapMode: label.wrapMode
|
||||
elide: label.elide
|
||||
text: label.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property int maximumWidth: (ApplicationInfo.constants.isMobile ? ApplicationInfo.applicationWidth : 1120) / 4
|
||||
Layout.minimumWidth: Math.min(Layout.maximumWidth, implicitWidth + 1)
|
||||
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
Label {
|
||||
id: label
|
||||
property int pixelSize: 34
|
||||
property real letterSpacing: -0.25
|
||||
font.family: "Open Sans"
|
||||
font.pixelSize: pixelSize * ApplicationInfo.ratio * 1.1 // increasing fonts
|
||||
font.letterSpacing: letterSpacing * ApplicationInfo.ratio
|
||||
color: ApplicationInfo.colors.doubleDarkGray
|
||||
verticalAlignment: Text.AlignBottom
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideRight
|
||||
linkColor: ApplicationInfo.colors.blue
|
||||
renderType: ApplicationInfo.isMobile ? Text.QtRendering : Text.NativeRendering
|
||||
function expectedTextWidth(value)
|
||||
{
|
||||
text.text = value
|
||||
return text.width
|
||||
}
|
||||
property int maximumWidth: (ApplicationInfo.constants.isMobile ? ApplicationInfo.applicationWidth : 1120) / 4
|
||||
Layout.minimumWidth: Math.min(Layout.maximumWidth, implicitWidth + 1)
|
||||
Text {
|
||||
id: text
|
||||
visible: false
|
||||
font.family: label.font.family
|
||||
font.pixelSize: label.font.pixelSize
|
||||
font.letterSpacing: label.font.letterSpacing
|
||||
wrapMode: label.wrapMode
|
||||
elide: label.elide
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Controls.Styles 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
ScrollView {
|
||||
frameVisible: false
|
||||
style: ScrollViewStyle {
|
||||
property int handleWidth: 20 * ApplicationInfo.ratio
|
||||
transientScrollBars: true
|
||||
padding{ top: 4 ; bottom: 4 ; right: 4}
|
||||
property bool hovered: false
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: ApplicationInfo.colors.white
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Controls.Styles 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
Slider {
|
||||
id: slider
|
||||
property real sliderHandleHeight: 0.
|
||||
property real sliderHandleWidth: 0.
|
||||
implicitHeight: sliderHandleHeight + ApplicationInfo.ratio * 25
|
||||
style: SliderStyle {
|
||||
groove: Rectangle {
|
||||
Rectangle {
|
||||
id: beforeHandle
|
||||
width: styleData.handlePosition
|
||||
height: 20 * ApplicationInfo.ratio
|
||||
color: ApplicationInfo.colors.blue
|
||||
radius: 90
|
||||
z: -1
|
||||
}
|
||||
Rectangle {
|
||||
id: afterHandle
|
||||
anchors.left: beforeHandle.right
|
||||
anchors.right: parent.right
|
||||
height: 20 * ApplicationInfo.ratio
|
||||
color: ApplicationInfo.colors.darkGray
|
||||
radius: 90
|
||||
z: -1
|
||||
}
|
||||
}
|
||||
handle: Item {
|
||||
width: sliderHandleWidth
|
||||
height: sliderHandleHeight
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
source: ApplicationInfo.imagePath(control.pressed ? "Pointer_pressed.png" : "Pointer.png")
|
||||
width: sliderHandleWidth + 16 * ApplicationInfo.ratio
|
||||
height: sliderHandleHeight + 16 * ApplicationInfo.ratio
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Controls.Styles 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
TextField {
|
||||
id: textfield
|
||||
signal clearButtonClicked
|
||||
implicitWidth: parent.width
|
||||
property bool isEmpty: true
|
||||
style: TextFieldStyle {
|
||||
renderType: ApplicationInfo.isMobile ? Text.QtRendering : Text.NativeRendering
|
||||
background: Rectangle {
|
||||
radius: 8
|
||||
border.width: 1
|
||||
border.color: Qt.darker(ApplicationInfo.colors.blue, 1.6)
|
||||
color: ApplicationInfo.colors.white
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0 ; color: "#ddd"}
|
||||
GradientStop { position: 0.05 ; color: "#fff"}
|
||||
}
|
||||
implicitHeight: 60 * ApplicationInfo.ratio
|
||||
opacity: 1
|
||||
}
|
||||
padding.left : (12 + 50) * ApplicationInfo.ratio
|
||||
padding.right: (12 + 50) * ApplicationInfo.ratio
|
||||
font.pixelSize: 28 * ApplicationInfo.ratio
|
||||
font.family: "Open Sans"
|
||||
font.letterSpacing: -0.25 * ApplicationInfo.ratio
|
||||
selectedTextColor : ApplicationInfo.colors.lightGray
|
||||
selectionColor : ApplicationInfo.colors.darkBlue
|
||||
textColor : ApplicationInfo.colors.mediumGray
|
||||
}
|
||||
|
||||
onClearButtonClicked: text = ""
|
||||
|
||||
Item {
|
||||
id: item
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
height: parent.height
|
||||
width: parent.height
|
||||
Image {
|
||||
opacity: 0.9
|
||||
anchors.centerIn: item
|
||||
height: iconSize
|
||||
width: iconSize
|
||||
source: ApplicationInfo.imagePath("magnifier.png")
|
||||
property int iconSize: 50 * ApplicationInfo.ratio
|
||||
}
|
||||
}
|
||||
|
||||
onTextChanged: isEmpty = (text === "")
|
||||
inputMethodHints: Qt.ImhNoPredictiveText
|
||||
MouseArea {
|
||||
z: 2
|
||||
opacity: !textfield.isEmpty ? 1 : 0
|
||||
Behavior on opacity {NumberAnimation{}}
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 4 * ApplicationInfo.ratio
|
||||
anchors.top: parent.top
|
||||
height: parent.height
|
||||
width: parent.height
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
source: ApplicationInfo.imagePath("clear.png")
|
||||
property int iconSize: 40 * ApplicationInfo.ratio
|
||||
opacity: parent.pressed ? 1 : 0.9
|
||||
width: iconSize
|
||||
height: iconSize
|
||||
}
|
||||
onClicked: textfield.clearButtonClicked()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
#include "./applicationinfo.h"
|
||||
|
||||
#include <qmath.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QGuiApplication>
|
||||
#include <QRegExp>
|
||||
#include <QScreen>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
|
||||
using namespace Io;
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
ApplicationInfo::ApplicationInfo()
|
||||
{
|
||||
m_colors = new QQmlPropertyMap(this);
|
||||
m_colors->insert(QLatin1String("white"), QVariant("#ffffff"));
|
||||
m_colors->insert(QLatin1String("smokeGray"), QVariant("#eeeeee"));
|
||||
m_colors->insert(QLatin1String("paleGray"), QVariant("#d7d6d5"));
|
||||
m_colors->insert(QLatin1String("lightGray"), QVariant("#aeadac"));
|
||||
m_colors->insert(QLatin1String("darkGray"), QVariant("#35322f"));
|
||||
m_colors->insert(QLatin1String("mediumGray"), QVariant("#5d5b59"));
|
||||
m_colors->insert(QLatin1String("doubleDarkGray"), QVariant("#1e1b18"));
|
||||
m_colors->insert(QLatin1String("blue"), QVariant("#14aaff"));
|
||||
m_colors->insert(QLatin1String("yetAnotherBlue"), QVariant("#428bca"));
|
||||
m_colors->insert(QLatin1String("darkBlue"), QVariant("#14148c"));
|
||||
m_colors->insert(QLatin1String("darkYellow"), QVariant("#dfdc00"));
|
||||
m_colors->insert(QLatin1String("darkYellow"), QVariant("#eb881c"));
|
||||
m_colors->insert(QLatin1String("almostBlack"), QVariant("#222222"));
|
||||
|
||||
m_constants = new QQmlPropertyMap(this);
|
||||
m_constants->insert(QLatin1String("isMobile"), QVariant(isMobile()));
|
||||
|
||||
QRect rect = QGuiApplication::primaryScreen()->geometry();
|
||||
m_ratio = isMobile() ? qMin(qMax(rect.width(), rect.height()) / 1136., qMin(rect.width(), rect.height()) / 640.) : .5;
|
||||
m_sliderHandleWidth = sizeWithRatio(70);
|
||||
m_sliderHandleHeight = sizeWithRatio(87);
|
||||
m_sliderGapWidth = sizeWithRatio(100);
|
||||
m_isPortraitMode = isMobile() ? rect.height() > rect.width() : false;
|
||||
m_hMargin = m_isPortraitMode ? 20 * ratio() : 50 * ratio();
|
||||
m_applicationWidth = isMobile() ? rect.width() : 1120;
|
||||
|
||||
m_constants->insert(QLatin1String("rowDelegateHeight"), QVariant(sizeWithRatio(118)));
|
||||
|
||||
m_fieldModel = new FieldModel(this);
|
||||
|
||||
if (isMobile()) {
|
||||
connect(QGuiApplication::primaryScreen(), &QScreen::orientationChanged, this, &ApplicationInfo::notifyPortraitMode);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationInfo::setApplicationWidth(const int newWidth)
|
||||
{
|
||||
if (newWidth != m_applicationWidth) {
|
||||
m_applicationWidth = newWidth;
|
||||
emit applicationWidthChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString ApplicationInfo::imagePath(const QString image)
|
||||
{
|
||||
return QStringLiteral("qrc:/qml/images/%1").arg(image);
|
||||
}
|
||||
|
||||
void ApplicationInfo::notifyPortraitMode(Qt::ScreenOrientation orientation)
|
||||
{
|
||||
switch (orientation) {
|
||||
case Qt::LandscapeOrientation:
|
||||
case Qt::InvertedLandscapeOrientation:
|
||||
setIsPortraitMode(false);
|
||||
break;
|
||||
case Qt::PortraitOrientation:
|
||||
case Qt::InvertedPortraitOrientation:
|
||||
setIsPortraitMode(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationInfo::setIsPortraitMode(const bool newMode)
|
||||
{
|
||||
if (m_isPortraitMode != newMode) {
|
||||
m_isPortraitMode = newMode;
|
||||
m_hMargin = m_isPortraitMode ? 20 * ratio() : 50 * ratio();
|
||||
emit portraitModeChanged();
|
||||
emit hMarginChanged();
|
||||
}
|
||||
}
|
||||
} // namespace QtGui
|
|
@ -1,176 +0,0 @@
|
|||
#ifndef APPLICATIONINFO_H
|
||||
#define APPLICATIONINFO_H
|
||||
|
||||
#include "../model/fieldmodel.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlPropertyMap>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class ApplicationInfo : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int applicationWidth READ applicationWidth WRITE setApplicationWidth NOTIFY applicationWidthChanged)
|
||||
Q_PROPERTY(bool isMobile READ isMobile CONSTANT)
|
||||
Q_PROPERTY(QObject *colors READ colors CONSTANT)
|
||||
Q_PROPERTY(QObject *constants READ constants CONSTANT)
|
||||
Q_PROPERTY(bool isPortraitMode READ isPortraitMode WRITE setIsPortraitMode NOTIFY portraitModeChanged)
|
||||
Q_PROPERTY(const QString ¤fFile READ currentFile WRITE setCurrentFile NOTIFY currentFileChanged)
|
||||
Q_PROPERTY(FieldModel *fieldModel READ fieldModel)
|
||||
Q_PROPERTY(Io::AccountEntry *currentAccountEntry READ currentAccountEntry WRITE setCurrentAccountEntry)
|
||||
Q_PROPERTY(QString currentAccountName READ currentAccountName)
|
||||
Q_PROPERTY(qreal ratio READ ratio CONSTANT)
|
||||
Q_PROPERTY(qreal hMargin READ hMargin NOTIFY hMarginChanged)
|
||||
Q_PROPERTY(qreal sliderHandleWidth READ sliderHandleWidth CONSTANT)
|
||||
Q_PROPERTY(qreal sliderHandleHeight READ sliderHandleHeight CONSTANT)
|
||||
Q_PROPERTY(qreal sliderGapWidth READ sliderGapWidth CONSTANT)
|
||||
|
||||
public:
|
||||
ApplicationInfo();
|
||||
|
||||
static constexpr bool isMobile();
|
||||
QQmlPropertyMap *colors() const;
|
||||
QQmlPropertyMap *constants() const;
|
||||
|
||||
int applicationWidth() const;
|
||||
void setApplicationWidth(const int newWidth);
|
||||
|
||||
bool isPortraitMode() const;
|
||||
void setIsPortraitMode(const bool newMode);
|
||||
|
||||
const QString ¤tFile() const;
|
||||
void setCurrentFile(const QString ¤tFile);
|
||||
FieldModel *fieldModel();
|
||||
Io::AccountEntry *currentAccountEntry();
|
||||
QString currentAccountName() const;
|
||||
void setCurrentAccountEntry(Io::AccountEntry *entry);
|
||||
|
||||
qreal hMargin() const;
|
||||
qreal ratio() const;
|
||||
qreal sliderHandleHeight();
|
||||
qreal sliderGapWidth();
|
||||
qreal sliderHandleWidth();
|
||||
|
||||
Q_INVOKABLE QString imagePath(const QString image);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void notifyPortraitMode(Qt::ScreenOrientation);
|
||||
|
||||
protected:
|
||||
qreal sizeWithRatio(const qreal height);
|
||||
|
||||
Q_SIGNALS:
|
||||
void currentFileChanged();
|
||||
void applicationWidthChanged();
|
||||
void portraitModeChanged();
|
||||
void hMarginChanged();
|
||||
|
||||
private:
|
||||
int m_applicationWidth;
|
||||
QQmlPropertyMap *m_colors;
|
||||
QQmlPropertyMap *m_constants;
|
||||
bool m_isPortraitMode;
|
||||
QString m_currentFile;
|
||||
FieldModel *m_fieldModel;
|
||||
qreal m_ratio;
|
||||
qreal m_hMargin;
|
||||
qreal m_sliderHandleHeight, m_sliderHandleWidth, m_sliderGapWidth;
|
||||
};
|
||||
|
||||
constexpr bool ApplicationInfo::isMobile()
|
||||
{
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_BLACKBERRY)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline QQmlPropertyMap *ApplicationInfo::colors() const
|
||||
{
|
||||
return m_colors;
|
||||
}
|
||||
|
||||
inline QQmlPropertyMap *ApplicationInfo::constants() const
|
||||
{
|
||||
return m_constants;
|
||||
}
|
||||
|
||||
inline int ApplicationInfo::applicationWidth() const
|
||||
{
|
||||
return m_applicationWidth;
|
||||
}
|
||||
|
||||
inline bool ApplicationInfo::isPortraitMode() const
|
||||
{
|
||||
return m_isPortraitMode;
|
||||
}
|
||||
|
||||
inline const QString &ApplicationInfo::currentFile() const
|
||||
{
|
||||
return m_currentFile;
|
||||
}
|
||||
|
||||
inline void ApplicationInfo::setCurrentFile(const QString ¤tFile)
|
||||
{
|
||||
if (m_currentFile != currentFile) {
|
||||
m_currentFile = currentFile;
|
||||
emit currentFileChanged();
|
||||
}
|
||||
}
|
||||
|
||||
inline FieldModel *ApplicationInfo::fieldModel()
|
||||
{
|
||||
return m_fieldModel;
|
||||
}
|
||||
|
||||
inline Io::AccountEntry *ApplicationInfo::currentAccountEntry()
|
||||
{
|
||||
return m_fieldModel->accountEntry();
|
||||
}
|
||||
|
||||
inline QString ApplicationInfo::currentAccountName() const
|
||||
{
|
||||
if (m_fieldModel->accountEntry()) {
|
||||
return QString::fromStdString(m_fieldModel->accountEntry()->label());
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
inline void ApplicationInfo::setCurrentAccountEntry(Io::AccountEntry *entry)
|
||||
{
|
||||
m_fieldModel->setAccountEntry(entry);
|
||||
}
|
||||
|
||||
inline qreal ApplicationInfo::hMargin() const
|
||||
{
|
||||
return m_hMargin;
|
||||
}
|
||||
|
||||
inline qreal ApplicationInfo::ratio() const
|
||||
{
|
||||
return m_ratio;
|
||||
}
|
||||
|
||||
inline qreal ApplicationInfo::sliderHandleHeight()
|
||||
{
|
||||
return m_sliderHandleHeight;
|
||||
}
|
||||
|
||||
inline qreal ApplicationInfo::sliderGapWidth()
|
||||
{
|
||||
return m_sliderGapWidth;
|
||||
}
|
||||
|
||||
inline qreal ApplicationInfo::sliderHandleWidth()
|
||||
{
|
||||
return m_sliderHandleWidth;
|
||||
}
|
||||
|
||||
inline qreal ApplicationInfo::sizeWithRatio(const qreal height)
|
||||
{
|
||||
return ratio() * height;
|
||||
}
|
||||
} // namespace QtGui
|
||||
|
||||
#endif // APPLICATIONINFO_H
|
|
@ -1,43 +0,0 @@
|
|||
#ifndef APPLICATIONPATHS_H
|
||||
#define APPLICATIONPATHS_H
|
||||
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
#include <QStringList>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class ApplicationPaths {
|
||||
public:
|
||||
static QString settingsPath();
|
||||
static QString dowloadedFilesPath();
|
||||
|
||||
protected:
|
||||
static QString path(QStandardPaths::StandardLocation location);
|
||||
};
|
||||
|
||||
inline QString ApplicationPaths::settingsPath()
|
||||
{
|
||||
return path(QStandardPaths::DataLocation);
|
||||
}
|
||||
|
||||
inline QString ApplicationPaths::dowloadedFilesPath()
|
||||
{
|
||||
return path(QStandardPaths::CacheLocation);
|
||||
}
|
||||
|
||||
inline QString ApplicationPaths::path(QStandardPaths::StandardLocation location)
|
||||
{
|
||||
QString path = QStandardPaths::standardLocations(location).value(0);
|
||||
QDir dir(path);
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(path);
|
||||
}
|
||||
if (!path.isEmpty() && !path.endsWith("/")) {
|
||||
path += "/";
|
||||
}
|
||||
return path;
|
||||
}
|
||||
} // namespace QtGui
|
||||
|
||||
#endif // APPLICATIONPATHS_H
|
|
@ -0,0 +1,145 @@
|
|||
#include "./controller.h"
|
||||
|
||||
#include <passwordfile/io/cryptoexception.h>
|
||||
#include <passwordfile/io/parsingexception.h>
|
||||
|
||||
#include <qtutilities/misc/dialogutils.h>
|
||||
|
||||
#include <c++utilities/io/catchiofailure.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QIcon>
|
||||
#include <QStringBuilder>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace std;
|
||||
using namespace Io;
|
||||
using namespace IoUtilities;
|
||||
using namespace Dialogs;
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
Controller::Controller(const QString &filePath, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_fileOpen(false)
|
||||
, m_fileModified(false)
|
||||
{
|
||||
setFilePath(filePath);
|
||||
m_entryFilterModel.setSourceModel(&m_entryModel);
|
||||
}
|
||||
|
||||
void Controller::setFilePath(const QString &filePath)
|
||||
{
|
||||
if (m_filePath == filePath) {
|
||||
return;
|
||||
}
|
||||
m_file.clear();
|
||||
m_file.setPath(filePath.toLocal8Bit().data());
|
||||
emit filePathChanged(m_filePath = filePath);
|
||||
}
|
||||
|
||||
void Controller::setPassword(const QString &password)
|
||||
{
|
||||
if (m_password == password) {
|
||||
return;
|
||||
}
|
||||
m_file.setPassword(password.toUtf8().data());
|
||||
emit passwordChanged(m_password = password);
|
||||
}
|
||||
|
||||
void Controller::load()
|
||||
{
|
||||
resetFileStatus();
|
||||
try {
|
||||
m_file.load();
|
||||
m_entryModel.setRootEntry(m_file.rootEntry());
|
||||
setFileOpen(true);
|
||||
updateWindowTitle();
|
||||
} catch (const CryptoException &e) {
|
||||
if (m_file.isEncryptionUsed() && m_password.isEmpty()) {
|
||||
emit passwordRequired(m_filePath);
|
||||
} else {
|
||||
emit fileError(tr("A crypto error occured when opening the file: ") + QString::fromLocal8Bit(e.what()));
|
||||
}
|
||||
} catch (const runtime_error &e) {
|
||||
emit fileError(tr("A parsing error occured when opening the file: ") + QString::fromLocal8Bit(e.what()));
|
||||
} catch (...) {
|
||||
emitIoError(tr("loading"));
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::create()
|
||||
{
|
||||
resetFileStatus();
|
||||
try {
|
||||
m_file.create();
|
||||
m_entryModel.setRootEntry(m_file.rootEntry());
|
||||
setFileOpen(true);
|
||||
updateWindowTitle();
|
||||
} catch (...) {
|
||||
emitIoError(tr("creating"));
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::close()
|
||||
{
|
||||
try {
|
||||
m_file.close();
|
||||
resetFileStatus();
|
||||
} catch (...) {
|
||||
emitIoError(tr("closing"));
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::save()
|
||||
{
|
||||
try {
|
||||
if (!m_password.isEmpty()) {
|
||||
const auto passwordUtf8(m_password.toUtf8());
|
||||
m_file.setPassword(string(passwordUtf8.data(), static_cast<size_t>(passwordUtf8.size())));
|
||||
} else {
|
||||
m_file.clearPassword();
|
||||
}
|
||||
m_file.save(!m_password.isEmpty());
|
||||
} catch (const CryptoException &e) {
|
||||
emit fileError(tr("A crypto error occured when saving the file: ") + QString::fromLocal8Bit(e.what()));
|
||||
} catch (const runtime_error &e) {
|
||||
emit fileError(tr("An internal error occured when saving the file: ") + QString::fromLocal8Bit(e.what()));
|
||||
} catch (...) {
|
||||
emitIoError(tr("saving"));
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::resetFileStatus()
|
||||
{
|
||||
setFileOpen(false);
|
||||
m_entryModel.reset();
|
||||
m_fieldModel.reset();
|
||||
}
|
||||
|
||||
void Controller::updateWindowTitle()
|
||||
{
|
||||
if (m_fileOpen) {
|
||||
const QFileInfo file(m_filePath);
|
||||
emit windowTitleChanged(m_windowTitle = file.fileName() % QStringLiteral(" - ") % file.dir().path());
|
||||
} else {
|
||||
emit windowTitleChanged(tr("No file opened."));
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::setFileOpen(bool fileOpen)
|
||||
{
|
||||
if (fileOpen != m_fileOpen) {
|
||||
emit fileOpenChanged(m_fileOpen = fileOpen);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::emitIoError(const QString &when)
|
||||
{
|
||||
const auto *const msg = catchIoFailure();
|
||||
emit fileError(tr("An IO error occured when %1 the file: ").arg(when) + QString::fromLocal8Bit(msg));
|
||||
}
|
||||
|
||||
} // namespace QtGui
|
|
@ -0,0 +1,130 @@
|
|||
#ifndef QT_QUICK_GUI_CONTROLLER_H
|
||||
#define QT_QUICK_GUI_CONTROLLER_H
|
||||
|
||||
#include "../model/entryfiltermodel.h"
|
||||
#include "../model/entrymodel.h"
|
||||
#include "../model/fieldmodel.h"
|
||||
|
||||
#include <passwordfile/io/passwordfile.h>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class Controller : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString filePath READ filePath WRITE setFilePath NOTIFY filePathChanged)
|
||||
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
|
||||
Q_PROPERTY(QString windowTitle READ windowTitle NOTIFY windowTitleChanged)
|
||||
Q_PROPERTY(bool fileOpen READ isFileOpen NOTIFY fileOpenChanged)
|
||||
Q_PROPERTY(EntryModel *entryModel READ entryModel NOTIFY entryModelChanged)
|
||||
Q_PROPERTY(EntryFilterModel *entryFilterModel READ entryFilterModel NOTIFY entryFilterModelChanged)
|
||||
Q_PROPERTY(FieldModel *fieldModel READ fieldModel NOTIFY fieldModelChanged)
|
||||
Q_PROPERTY(QModelIndex currentAccountIndex READ currentAccountIndex WRITE setCurrentAccountIndex NOTIFY currentAccountChanged)
|
||||
Q_PROPERTY(QString currentAccountName READ currentAccountName NOTIFY currentAccountChanged)
|
||||
|
||||
public:
|
||||
explicit Controller(const QString &filePath = QString(), QObject *parent = nullptr);
|
||||
|
||||
const QString &filePath() const;
|
||||
void setFilePath(const QString &filePath);
|
||||
const QString &password() const;
|
||||
void setPassword(const QString &password);
|
||||
const QString &windowTitle() const;
|
||||
bool isFileOpen() const;
|
||||
EntryModel *entryModel();
|
||||
EntryFilterModel *entryFilterModel();
|
||||
FieldModel *fieldModel();
|
||||
QModelIndex currentAccountIndex() const;
|
||||
void setCurrentAccountIndex(const QModelIndex &accountIndex);
|
||||
QString currentAccountName() const;
|
||||
|
||||
public slots:
|
||||
void load();
|
||||
void create();
|
||||
void close();
|
||||
void save();
|
||||
|
||||
signals:
|
||||
void filePathChanged(const QString &newFilePath);
|
||||
void passwordChanged(const QString &newPassword);
|
||||
void passwordRequired(const QString &filePath);
|
||||
void windowTitleChanged(const QString &windowTitle);
|
||||
void fileOpenChanged(bool fileOpen);
|
||||
void fileError(const QString &errorMessage);
|
||||
void entryModelChanged();
|
||||
void entryFilterModelChanged();
|
||||
void fieldModelChanged();
|
||||
void currentAccountChanged();
|
||||
|
||||
private:
|
||||
void resetFileStatus();
|
||||
void updateWindowTitle();
|
||||
void setFileOpen(bool fileOpen);
|
||||
void emitIoError(const QString &when);
|
||||
|
||||
QString m_filePath;
|
||||
QString m_password;
|
||||
QString m_windowTitle;
|
||||
Io::PasswordFile m_file;
|
||||
EntryModel m_entryModel;
|
||||
EntryFilterModel m_entryFilterModel;
|
||||
FieldModel m_fieldModel;
|
||||
bool m_fileOpen;
|
||||
bool m_fileModified;
|
||||
};
|
||||
|
||||
inline const QString &Controller::filePath() const
|
||||
{
|
||||
return m_filePath;
|
||||
}
|
||||
|
||||
inline const QString &Controller::password() const
|
||||
{
|
||||
return m_password;
|
||||
}
|
||||
|
||||
inline const QString &Controller::windowTitle() const
|
||||
{
|
||||
return m_windowTitle;
|
||||
}
|
||||
|
||||
inline bool Controller::isFileOpen() const
|
||||
{
|
||||
return m_fileOpen;
|
||||
}
|
||||
|
||||
inline EntryModel *Controller::entryModel()
|
||||
{
|
||||
return &m_entryModel;
|
||||
}
|
||||
|
||||
inline EntryFilterModel *Controller::entryFilterModel()
|
||||
{
|
||||
return &m_entryFilterModel;
|
||||
}
|
||||
|
||||
inline FieldModel *Controller::fieldModel()
|
||||
{
|
||||
return &m_fieldModel;
|
||||
}
|
||||
|
||||
inline QModelIndex Controller::currentAccountIndex() const
|
||||
{
|
||||
return m_fieldModel.accountEntry() ? m_entryModel.index(const_cast<Io::AccountEntry *>(m_fieldModel.accountEntry())) : QModelIndex();
|
||||
}
|
||||
|
||||
inline void Controller::setCurrentAccountIndex(const QModelIndex &accountIndex)
|
||||
{
|
||||
m_fieldModel.setAccountEntry(m_entryModel.isNode(accountIndex) ? nullptr : static_cast<Io::AccountEntry *>(m_entryModel.entry(accountIndex)));
|
||||
emit currentAccountChanged();
|
||||
}
|
||||
|
||||
inline QString Controller::currentAccountName() const
|
||||
{
|
||||
return m_fieldModel.accountEntry() ? QString::fromStdString(m_fieldModel.accountEntry()->label()) : QStringLiteral("?");
|
||||
}
|
||||
|
||||
} // namespace QtGui
|
||||
|
||||
#endif // QT_QUICK_GUI_CONTROLLER_H
|
|
@ -1,37 +1,25 @@
|
|||
#include "./initiatequick.h"
|
||||
#include "./applicationinfo.h"
|
||||
|
||||
#include "../model/entryfiltermodel.h"
|
||||
#include "../model/entrymodel.h"
|
||||
#include "../model/fieldmodel.h"
|
||||
#include "./controller.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
#include <qtutilities/resources/qtconfigarguments.h>
|
||||
#include <qtutilities/resources/resources.h>
|
||||
|
||||
#if defined(GUI_QTWIDGETS)
|
||||
#include <QApplication>
|
||||
#else
|
||||
#include <QGuiApplication>
|
||||
#endif
|
||||
#include <QDebug>
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QTextCodec>
|
||||
#include <QtQml>
|
||||
|
||||
#ifdef PASSWORD_MANAGER_GUI_QTWIDGETS
|
||||
#include <QApplication>
|
||||
#endif
|
||||
|
||||
using namespace ApplicationUtilities;
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
static QObject *applicationInfo(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
{
|
||||
Q_UNUSED(engine)
|
||||
Q_UNUSED(scriptEngine)
|
||||
return new ApplicationInfo();
|
||||
}
|
||||
|
||||
int runQuickGui(int argc, char *argv[], const QtConfigArguments &qtConfigArgs, const QString &file)
|
||||
{
|
||||
// init application
|
||||
|
@ -40,7 +28,7 @@ int runQuickGui(int argc, char *argv[], const QtConfigArguments &qtConfigArgs, c
|
|||
#endif
|
||||
SET_QT_APPLICATION_INFO;
|
||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
#if defined(GUI_QTWIDGETS)
|
||||
#ifdef PASSWORD_MANAGER_GUI_QTWIDGETS
|
||||
QApplication a(argc, argv);
|
||||
#else
|
||||
QGuiApplication a(argc, argv);
|
||||
|
@ -64,16 +52,19 @@ int runQuickGui(int argc, char *argv[], const QtConfigArguments &qtConfigArgs, c
|
|||
};
|
||||
|
||||
// init Quick GUI
|
||||
qmlRegisterSingletonType<QtGui::ApplicationInfo>("martchus.passwordmanager", 2, 0, "ApplicationInfo", applicationInfo);
|
||||
qmlRegisterType<QtGui::EntryFilterModel>("martchus.passwordmanager", 2, 0, "EntryFilterModel");
|
||||
qmlRegisterType<QtGui::EntryModel>("martchus.passwordmanager", 2, 0, "EntryModel");
|
||||
qmlRegisterType<QtGui::FieldModel>("martchus.passwordmanager", 2, 0, "FieldModel");
|
||||
QQmlApplicationEngine engine(QUrl("qrc:/qml/main.qml"));
|
||||
engine.rootContext()->setContextProperty(QStringLiteral("userPaths"), userPaths);
|
||||
engine.rootContext()->setContextProperty(QStringLiteral("file"), file);
|
||||
//qmlRegisterType<QtGui::EntryFilterModel>("martchus.passwordmanager", 2, 0, "EntryFilterModel");
|
||||
//qmlRegisterType<QtGui::EntryModel>("martchus.passwordmanager", 2, 0, "EntryModel");
|
||||
//qmlRegisterType<QtGui::FieldModel>("martchus.passwordmanager", 2, 0, "FieldModel");
|
||||
//qmlRegisterType<Io::AccountEntry>("martchus.passwordmanager", 2, 1, "AccountEntry");
|
||||
|
||||
// start event loop
|
||||
int res = a.exec();
|
||||
return res;
|
||||
QQmlApplicationEngine engine;
|
||||
Controller controller(file);
|
||||
QQmlContext *const context(engine.rootContext());
|
||||
context->setContextProperty(QStringLiteral("userPaths"), userPaths);
|
||||
context->setContextProperty(QStringLiteral("nativeInterface"), &controller);
|
||||
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
|
||||
|
||||
// run event loop
|
||||
return a.exec();
|
||||
}
|
||||
} // namespace QtGui
|
||||
|
|
|
@ -1,20 +1,9 @@
|
|||
<RCC>
|
||||
<qresource prefix="/qml">
|
||||
<file alias="main.qml">../qml/main.qml</file>
|
||||
<file alias="pages/AccountsPage.qml">../qml/pages/AccountsPage.qml</file>
|
||||
<file alias="pages/BasicPage.qml">../qml/pages/BasicPage.qml</file>
|
||||
<file alias="pages/FieldsPage.qml">../qml/pages/FieldsPage.qml</file>
|
||||
<file alias="pages/StartPage.qml">../qml/pages/StartPage.qml</file>
|
||||
<file alias="pages/Separator.qml">../qml/pages/Separator.qml</file>
|
||||
<file alias="touch/TouchButton.qml">../qml/touch/TouchButton.qml</file>
|
||||
<file alias="touch/TouchLabel.qml">../qml/touch/TouchLabel.qml</file>
|
||||
<file alias="touch/TouchTextField.qml">../qml/touch/TouchTextField.qml</file>
|
||||
<file alias="touch/TouchSlider.qml">../qml/touch/TouchSlider.qml</file>
|
||||
<file alias="touch/TouchScrollView.qml">../qml/touch/TouchScrollView.qml</file>
|
||||
<file alias="touch/ListViewDelegate.qml">../qml/touch/ListViewDelegate.qml</file>
|
||||
<file alias="images/clear.png">../qml/images/clear.png</file>
|
||||
<file alias="images/magnifier.png">../qml/images/magnifier.png</file>
|
||||
<file alias="images/backarrow.png">../qml/images/backarrow.png</file>
|
||||
<file alias="images/close.png">../qml/images/close.png</file>
|
||||
<file alias="BasicDialog.qml">../qml/BasicDialog.qml</file>
|
||||
<file alias="PasswordDialog.qml">../qml/PasswordDialog.qml</file>
|
||||
<file alias="EntriesPage.qml">../qml/EntriesPage.qml</file>
|
||||
<file alias="banner.png">../qml/banner.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
Before Width: | Height: | Size: 376 B After Width: | Height: | Size: 376 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 470 KiB |