yubioath-flutter/qml/main.qml
2020-06-15 09:10:33 +02:00

324 lines
10 KiB
QML

import Qt.labs.platform 1.1
import Qt.labs.settings 1.0
import QtQml 2.12
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2
import QtQuick.Layouts 1.3
import QtQuick.Window 2.2
ApplicationWindow {
id: app
readonly property string yubicoGreen: isDark() ? "#b1cf77" : "#9aca3c"
readonly property string yubicoWhite: "#ffffff"
readonly property string yubicoRed: isDark() ? "#cf6679" : "#b00020"
property string primaryColor: isDark() ? "#ffffff" : "#303030"
readonly property string defaultBackground: isDark() ? "#303030" : "#f7f8f9"
readonly property string defaultElevated: isDark() ? "#383838" : "#ffffff"
readonly property string defaultImageOverlay: isDark() ? "#565656" : "#dddddd"
readonly property string defaultForeground: isDark() ? "#fafafa" : "#565656"
property string formUnderline: isDark() ? "#737373" : "#d8d8d8"
property string formText: isDark() ? "#f0f0f0" : "#606060"
property string formPlaceholderText: isDark() ? "#808080" : "#b0b0b0"
property string formImageOverlay: isDark() ? "#d8d8d8" : "#727272"
property string formStepBackground: isDark() ? "#565656" : "#bbbbbb"
property string formHighlightItem: isDark() ? "#4a4a4a" : "#e9e9e9"
property string formButtonBorder: isDark() ? "#5f6368" : "#dadce0"
property string toolTipForeground: app.isDark() ? "#fafafa" : "#fbfbfb"
property string toolTipBackground: app.isDark() ? "#4a4a4a" : "#7f7f7f"
property var fullEmphasis: 1
property var highEmphasis: 0.87
property var lowEmphasis: 0.6
property var disabledEmphasis: 0.38
property var cardSelectedEmphasis: 0.08
property var cardHoveredEmphasis: 0.05
property var cardNormalEmphasis: 0.03
property var currentCredentialCard
property string iconFavorite: "#f7bd0c"
// Don't refresh credentials when window is minimized or hidden
// See http://doc.qt.io/qt-5/qwindow.html#Visibility-enum
property bool isInForeground: visibility != 3 && visibility != 0
function enableLogging(logLevel) {
yubiKey.enableLogging(logLevel, null);
}
function enableLoggingToFile(logLevel, logFile) {
yubiKey.enableLogging(logLevel, logFile);
}
function disableLogging() {
yubiKey.disableLogging();
}
function isDark() {
return app.Material.theme === Material.Dark;
}
function saveScreenLayout() {
settings.desktopAvailableWidth = Screen.desktopAvailableWidth;
settings.desktopAvailableHeight = Screen.desktopAvailableHeight;
}
function ensureMinimumWindowSize() {
app.width = width < minimumWidth ? minimumWidth : width;
app.height = height < minimumHeight ? minimumHeight : height;
}
function ensureValidWindowPosition() {
// If we have the same desktop dimensions as last time, use the saved position.
// If not, put the window in the middle of the screen.
var savedScreenLayout = (settings.desktopAvailableWidth === Screen.desktopAvailableWidth) && (settings.desktopAvailableHeight === Screen.desktopAvailableHeight);
app.x = (savedScreenLayout) ? settings.x : Screen.width / 2 - app.width / 2;
app.y = (savedScreenLayout) ? settings.y : Screen.height / 2 - app.height / 2;
// If app.x and app.y are outside of the available screen geometry,
// put the app in the middle of the screen.
if (!isWindowPositionInsideSomeMonitor() && (Qt.platform.os == "windows")) {
app.x = Screen.width / 2 - app.width / 2;
app.y = Screen.height / 2 - app.height / 2;
}
}
function isWindowPositionInsideSomeMonitor() {
for (var i = 0; i < monitorAreas.length; i++) {
var xMin = monitorAreas[i].xMin;
var xMax = monitorAreas[i].xMax;
var yMin = monitorAreas[i].yMin;
var yMax = monitorAreas[i].yMax;
if (app.x > xMin && app.x < xMax)
if (app.y > yMin && app.y < yMax)
return true;
}
return false;
}
function updateTrayVisibility() {
// When the tray option is enabled, closing the last window
// doesn't actually close the application.
application.quitOnLastWindowClosed = !settings.closeToTray;
}
function calculateFavorite(credential, text) {
if (credential && credential.touch)
sysTrayIcon.showMessage(qsTr("Touch required"), qsTr("Touch your YubiKey now to generate security code."), SystemTrayIcon.NoIcon);
yubiKey.calculate(credential, function(resp) {
if (resp.success) {
clipBoard.push(resp.code.value);
sysTrayIcon.showMessage(qsTr("Copied to clipboard"), "The code for " + text + " is now in the clipboard.", SystemTrayIcon.NoIcon);
} else {
sysTrayIcon.showMessage(qsTr("Error"), "calculate failed: " + resp.error_id, SystemTrayIcon.NoIcon);
console.log("calculate failed:", resp.error_id);
}
});
}
function colorizeMatch(string, query) {
return string.replace(escapeRegExp(query.trim(), "gi"), "<span style=\"background-color:'#ffeb3b';color:'#333333';\">$&</span>") + " ";
}
function escapeRegExp(string, flags) {
return RegExp(string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, "|"), flags);
}
function getFavoriteEntries() {
var favs = entriesComponent.createObject(app, {
});
for (var i = 0; i < entries.count; i++) {
var entry = entries.get(i);
if (!!entry.credential && settings.favorites.includes(entry.credential.key))
favs.append(entry);
}
return favs;
}
width: 300
height: 502
minimumWidth: 300
minimumHeight: 348
visible: false
flags: Qt.Window | Qt.WindowFullscreenButtonHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint | Qt.WindowFullscreenButtonHint
Material.theme: settings.theme
Material.primary: yubicoGreen
Material.accent: yubicoGreen
Material.foreground: defaultForeground
Material.background: defaultBackground
onIsInForegroundChanged: {
(poller.running = isInForeground || settings.closeToTray);
}
Component.onCompleted: {
updateTrayVisibility();
ensureMinimumWindowSize();
ensureValidWindowPosition();
app.visible = !(settings.closeToTray && settings.hideOnLaunch);
}
Component.onDestruction: saveScreenLayout()
font.family: robotoRegular.name
FontLoader {
id: robotoRegular
source: "../fonts/Roboto-Regular.ttf"
}
FontLoader {
id: robotoBold
source: "../fonts/Roboto-Bold.ttf"
}
FontLoader {
id: robotoItalic
source: "../fonts/Roboto-Italic.ttf"
}
FontLoader {
id: robotoMedium
source: "../fonts/Roboto-Medium.ttf"
}
FontLoader {
id: robotoLight
source: "../fonts/Roboto-Light.ttf"
}
Shortcut {
sequence: StandardKey.Copy
enabled: !!currentCredentialCard
onActivated: app.currentCredentialCard.calculateCard(true)
}
Shortcut {
sequence: StandardKey.Delete
enabled: !!currentCredentialCard
onActivated: app.currentCredentialCard.deleteCard()
}
Shortcut {
sequence: "Ctrl+D" // This becomes Cmd + D on macOS
enabled: !!currentCredentialCard
onActivated: app.currentCredentialCard.toggleFavorite()
}
Shortcut {
sequence: StandardKey.Open
enabled: !!yubiKey.currentDevice && yubiKey.currentDeviceValidated
onActivated: navigator.goToNewCredential()
}
Shortcut {
sequence: StandardKey.Find
onActivated: toolBar.searchField.forceActiveFocus()
}
Shortcut {
sequence: StandardKey.Preferences
onActivated: navigator.goToSettings()
}
Shortcut {
sequence: StandardKey.Quit
context: Qt.ApplicationShortcut
onActivated: Qt.quit()
}
Shortcut {
sequence: StandardKey.Close
onActivated: app.close()
context: Qt.ApplicationShortcut
}
Shortcut {
sequence: StandardKey.Italic
onActivated: navigator.about()
context: Qt.ApplicationShortcut
}
Shortcut {
sequence: StandardKey.FullScreen
onActivated: visibility = visibility === Window.FullScreen ? Window.Windowed : Window.FullScreen
context: Qt.ApplicationShortcut
}
// This information is stored in the system registry on Windows,
// and in XML preferences files on macOS. On other Unix systems,
// in the absence of a standard, INI text files are used.
// See http://doc.qt.io/qt-5/qml-qt-labs-settings-settings.html#details
Settings {
id: settings
property bool useCustomReader
property string customReaderName
property bool closeToTray
property bool hideOnLaunch
property bool requireTouch: true
property int theme: Material.System
// Keep track of window and desktop dimensions.
property alias width: app.width
property alias height: app.height
property alias x: app.x
property alias y: app.y
property int desktopAvailableWidth
property int desktopAvailableHeight
property var favorites: []
onCloseToTrayChanged: updateTrayVisibility()
onThemeChanged: {
app.Material.theme = theme;
}
}
Component {
id: entriesComponent
EntriesModel {
}
}
EntriesModel {
id: entries
}
ClipBoard {
id: clipBoard
}
Timer {
id: poller
triggeredOnStart: true
interval: 1000
repeat: true
running: app.isInForeground || settings.closeToTray
onTriggered: yubiKey.poll()
}
YubiKey {
id: yubiKey
}
SystemTray {
id: sysTrayIcon
}
Navigator {
id: navigator
anchors.fill: parent
focus: true
}
header: StyledToolBar {
id: toolBar
}
}