From cb1071b3ad721eac37a7c2b7c9cdd1369506f579 Mon Sep 17 00:00:00 2001 From: Martchus Date: Thu, 25 Nov 2021 20:27:58 +0100 Subject: [PATCH] Improve Plasmoid to make it look more like official Plasmoids * Use header (with integrated buttons when shown as part of the system tray Plasmoid) * Use less space so it fits into the system tray plasmoid (at least on a full HD screen with 96 dpi) * Show action for internal errors only if there are internal errors (like in the Qt Widgets based GUI) * Port away from deprecated tab bar (which is not used in official Plasmoids anymore as well) * Simplify code for ensuring the minimum size as configured (still does not work within the system tray Plasmoid) --- plasmoid/CMakeLists.txt | 2 + plasmoid/cmake/templates/metadata.desktop.in | 2 + plasmoid/lib/syncthingapplet.cpp | 10 + plasmoid/lib/syncthingapplet.h | 4 + .../package/contents/ui/DirectoriesPage.qml | 19 +- .../contents/ui/FullRepresentation.qml | 824 ++++++------------ plasmoid/package/contents/ui/TabButton.qml | 29 + plasmoid/package/contents/ui/ToolBar.qml | 279 ++++++ plasmoid/package/contents/ui/main.qml | 36 +- plasmoid/testing.md | 2 + 10 files changed, 604 insertions(+), 603 deletions(-) create mode 100644 plasmoid/package/contents/ui/TabButton.qml create mode 100644 plasmoid/package/contents/ui/ToolBar.qml diff --git a/plasmoid/CMakeLists.txt b/plasmoid/CMakeLists.txt index fd2263b..2b568e8 100644 --- a/plasmoid/CMakeLists.txt +++ b/plasmoid/CMakeLists.txt @@ -20,8 +20,10 @@ set(PLASMOID_FILES package/contents/ui/TopLevelItem.qml package/contents/ui/DetailView.qml package/contents/ui/DetailItem.qml + package/contents/ui/TabButton.qml package/contents/ui/ToolTipTrigger.qml package/contents/ui/ToolTipView.qml + package/contents/ui/ToolBar.qml package/contents/ui/ToolButton.qml package/contents/ui/TinyButton.qml package/contents/ui/IconLabel.qml diff --git a/plasmoid/cmake/templates/metadata.desktop.in b/plasmoid/cmake/templates/metadata.desktop.in index a6929d5..f99a0d0 100644 --- a/plasmoid/cmake/templates/metadata.desktop.in +++ b/plasmoid/cmake/templates/metadata.desktop.in @@ -15,6 +15,8 @@ X-KDE-ServiceTypes=Plasma/Applet X-Plasma-NotificationArea=true X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml +X-Plasma-NotificationArea=true +X-Plasma-NotificationAreaCategory=SystemServices X-Plasma-RemoteLocation= X-KDE-PluginInfo-EnabledByDefault=true X-KDE-PluginInfo-Category=System Information diff --git a/plasmoid/lib/syncthingapplet.cpp b/plasmoid/lib/syncthingapplet.cpp index e84b474..5c1e687 100644 --- a/plasmoid/lib/syncthingapplet.cpp +++ b/plasmoid/lib/syncthingapplet.cpp @@ -65,6 +65,7 @@ SyncthingApplet::SyncthingApplet(QObject *parent, const QVariantList &data) , m_webViewDlg(nullptr) #endif , m_currentConnectionConfig(-1) + , m_hasInternalErrors(false) , m_initialized(false) { #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD @@ -248,6 +249,11 @@ bool SyncthingApplet::isStartStopEnabled() const #endif } +bool SyncthingApplet::hasInternalErrors() const +{ + return m_hasInternalErrors; +} + bool SyncthingApplet::areNotificationsAvailable() const { return !m_notifications.empty(); @@ -471,6 +477,9 @@ void SyncthingApplet::handleInternalError( InternalError error(errorMsg, request.url(), response); m_dbusNotifier.showInternalError(error); InternalErrorsDialog::addError(move(error)); + if (!m_hasInternalErrors) { + emit hasInternalErrorsChanged(m_hasInternalErrors = true); + } } void SyncthingApplet::handleDirStatisticsChanged() @@ -481,6 +490,7 @@ void SyncthingApplet::handleDirStatisticsChanged() void SyncthingApplet::handleErrorsCleared() { + emit hasInternalErrorsChanged(m_hasInternalErrors = false); } void SyncthingApplet::handleAboutDialogDeleted() diff --git a/plasmoid/lib/syncthingapplet.h b/plasmoid/lib/syncthingapplet.h index 705adfe..10c2709 100644 --- a/plasmoid/lib/syncthingapplet.h +++ b/plasmoid/lib/syncthingapplet.h @@ -65,6 +65,7 @@ class SyncthingApplet : public Plasma::Applet { Q_PROPERTY(int currentConnectionConfigIndex READ currentConnectionConfigIndex WRITE setCurrentConnectionConfigIndex NOTIFY currentConnectionConfigIndexChanged) Q_PROPERTY(bool startStopEnabled READ isStartStopEnabled NOTIFY settingsChanged) + Q_PROPERTY(bool hasInternalErrors READ hasInternalErrors NOTIFY hasInternalErrorsChanged) Q_PROPERTY(QSize size READ size WRITE setSize NOTIFY sizeChanged) Q_PROPERTY(bool notificationsAvailable READ areNotificationsAvailable NOTIFY notificationsAvailableChanged) Q_PROPERTY(bool passive READ isPassive NOTIFY passiveChanged) @@ -102,6 +103,7 @@ public: Data::SyncthingConnectionSettings *connectionConfig(int index); void setCurrentConnectionConfigIndex(int index); bool isStartStopEnabled() const; + bool hasInternalErrors() const; QSize size() const; void setSize(const QSize &size); bool areNotificationsAvailable() const; @@ -147,6 +149,7 @@ Q_SIGNALS: void statisticsChanged(); void settingsChanged(); void currentConnectionConfigIndexChanged(int index); + void hasInternalErrorsChanged(bool hasInternalErrors); void sizeChanged(const QSize &size); void notificationsAvailableChanged(bool notificationsAvailable); void passiveChanged(bool passive); @@ -193,6 +196,7 @@ private: QtGui::WebViewDialog *m_webViewDlg; #endif int m_currentConnectionConfig; + bool m_hasInternalErrors; bool m_initialized; QSize m_size; }; diff --git a/plasmoid/package/contents/ui/DirectoriesPage.qml b/plasmoid/package/contents/ui/DirectoriesPage.qml index f437bca..c006c4b 100644 --- a/plasmoid/package/contents/ui/DirectoriesPage.qml +++ b/plasmoid/package/contents/ui/DirectoriesPage.qml @@ -13,6 +13,16 @@ ColumnLayout { anchors.fill: parent objectName: "DirectoriesPage" + PlasmaComponents3.TextField { + property bool explicitelyShown: false + id: filter + clearButtonShown: true + Layout.fillWidth: true + visible: explicitelyShown || text !== "" + placeholderText: qsTr("Filter directories") + onTextChanged: directoryView.model.filterRegularExpression = new RegExp(text) + } + PlasmaExtras.ScrollArea { Layout.fillWidth: true Layout.fillHeight: true @@ -170,13 +180,4 @@ ColumnLayout { } } } - - PlasmaComponents3.TextField { - property bool explicitelyShown: false - id: filter - clearButtonShown: true - Layout.fillWidth: true - visible: explicitelyShown || text !== "" - onTextChanged: directoryView.model.filterRegularExpression = new RegExp(text) - } } diff --git a/plasmoid/package/contents/ui/FullRepresentation.qml b/plasmoid/package/contents/ui/FullRepresentation.qml index e8b650c..b1c43fa 100644 --- a/plasmoid/package/contents/ui/FullRepresentation.qml +++ b/plasmoid/package/contents/ui/FullRepresentation.qml @@ -1,43 +1,36 @@ -import QtQuick 2.8 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 2.15 as QQ2 // for ComboBox (PlasmaComponents3 version clips the end of the text) import QtQml 2.2 +import QtQuick 2.8 +import QtQuick.Layouts 1.15 +import QtQuick.Controls 2.15 as QQ2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.extras 2.0 as PlasmaExtras -import org.kde.plasma.components 2.0 as PlasmaComponents // for vertical TabBar import org.kde.plasma.components 3.0 as PlasmaComponents3 -import martchus.syncthingplasmoid 0.6 as SyncthingPlasmoid -ColumnLayout { - id: root - - // ensure keyboard events can be received after initialization - Component.onCompleted: forceActiveFocus() - - // update the size when settings changed - Connections { - target: plasmoid.nativeInterface - onSizeChanged: { - switch (plasmoid.location) { - case PlasmaCore.Types.Floating: - case PlasmaCore.Types.TopEdge: - case PlasmaCore.Types.BottomEdge: - case PlasmaCore.Types.LeftEdge: - case PlasmaCore.Types.RightEdge: - // set the parent's width and height so it will shrink again when decreasing the size - var size = plasmoid.nativeInterface.size - parent.width = units.gridUnit * size.width - parent.height = units.gridUnit * size.height - break - default: - ; - } +PlasmaComponents3.Page { + header: PlasmaExtras.PlasmoidHeading { + ToolBar { + id: toolbar + width: parent.width } } + // define functions to locate the current page and filter + function findCurrentPage() { + switch (tabBar.currentIndex) { + case 0: return directoriesPage + case 1: return devicesPage + case 2: return downloadsPage + case 3: return recentChangesPage + default: return directoriesPage + } + } + function findCurrentFilter() { + return findCurrentPage().filter + } + // define shortcuts to trigger actions for currently selected item function clickCurrentItemButton(buttonName) { - mainTabGroup.currentTab.item.view.clickCurrentItemButton(buttonName) + findCurrentPage().view.clickCurrentItemButton(buttonName) } Shortcut { sequence: "Ctrl+R" @@ -52,569 +45,248 @@ ColumnLayout { onActivated: clickCurrentItemButton("openButton") } - // define custom key handling for switching tabs, selecting items and filtering - Keys.onPressed: { - // note: event only received after clicking the tab buttons in plasmoidviewer - // but works as expected in plasmashell - switch (event.key) { - case Qt.Key_Up: - switch (event.modifiers) { - case Qt.NoModifier: - // select previous item in current tab - mainTabGroup.currentTab.item.view.decrementCurrentIndex() - break - case Qt.ShiftModifier: - // select previous connection - --plasmoid.nativeInterface.currentConnectionConfigIndex - break - } - break - case Qt.Key_Down: - switch (event.modifiers) { - case Qt.NoModifier: - // select next item in current tab - mainTabGroup.currentTab.item.view.incrementCurrentIndex() - break - case Qt.ShiftModifier: - // select previous connection - ++plasmoid.nativeInterface.currentConnectionConfigIndex - break - } - break - case Qt.Key_Left: - // select previous tab - switch (mainTabGroup.currentTab) { - case dirsPage: - recentChangesPage.clicked() - break - case devicesPage: - dirsTabButton.clicked() - break - case downloadsPage: - devsTabButton.clicked() - break - case recentChangesPage: - downloadsTabButton.clicked() - break - } - break - case Qt.Key_Right: - // select next tab - switch (mainTabGroup.currentTab) { - case dirsPage: - devsTabButton.clicked() - break - case devicesPage: - downloadsTabButton.clicked() - break - case downloadsPage: - recentChangesTabButton.clicked() - break - case recentChangesPage: - dirsTabButton.clicked() - } - break - case Qt.Key_Enter: - - // fallthrough - case Qt.Key_Return: - // toggle expanded state of current item - var currentItem = mainTabGroup.currentTab.item.view.currentItem - if (currentItem) { - currentItem.expanded = !currentItem.expanded - } - break - case Qt.Key_Escape: - var filter = findCurrentFilter() - if (filter && filter.text !== "") { - // reset filter - filter.explicitelyShown = false - filter.text = "" - event.accepted = true - } else { - // hide plasmoid - plasmoid.expanded = false - } - break - case Qt.Key_1: - dirsTabButton.clicked() - break - case Qt.Key_2: - devsTabButton.clicked() - break - case Qt.Key_3: - downloadsTabButton.clicked() - break - case Qt.Key_4: - recentChangesTabButton.clicked() - break - default: - sendKeyEventToFilter(event) - return - } - event.accepted = true - } - - function findCurrentFilter() { - return mainTabGroup.currentTab.item.filter - } - - function sendKeyEventToFilter(event) { - var filter = findCurrentFilter() - if (!filter || event.text === "" || filter.activeFocus) { - return - } - if (event.key === Qt.Key_Backspace && filter.text === "") { - filter.explicitelyShown = false - return - } - if (event.matches(StandardKey.Paste)) { - filter.paste() - } else { - filter.text = "" - filter.text += event.text - } - filter.forceActiveFocus() - } - - // heading and right-corner buttons - RowLayout { - id: toolBar - Layout.fillWidth: true - Layout.minimumHeight: units.iconSizes.medium - Layout.maximumHeight: units.iconSizes.medium - - ToolButton { - id: connectButton - states: [ - State { - name: "disconnected" - PropertyChanges { - target: connectButton - text: qsTr("Connect") - icon.source: "image://fa/refresh" - visible: true - } - }, - State { - name: "connecting" - PropertyChanges { - target: connectButton - visible: false - } - }, - State { - name: "paused" - PropertyChanges { - target: connectButton - text: qsTr("Resume") - icon.source: "image://fa/play" - visible: true - } - }, - State { - name: "idle" - PropertyChanges { - target: connectButton - text: qsTr("Pause") - icon.source: "image://fa/pause" - visible: true - } - } - ] - state: { - switch (plasmoid.nativeInterface.connection.status) { - case SyncthingPlasmoid.Data.Disconnected: - return "disconnected" - case SyncthingPlasmoid.Data.Reconnecting: - return "connecting"; - case SyncthingPlasmoid.Data.Paused: - return "paused" - default: - return "idle" - } - } - PlasmaComponents3.ToolTip { - text: connectButton.text - } - onClicked: { - switch (plasmoid.nativeInterface.connection.status) { - case SyncthingPlasmoid.Data.Disconnected: - plasmoid.nativeInterface.connection.connect() - break - case SyncthingPlasmoid.Data.Reconnecting: - break - case SyncthingPlasmoid.Data.Paused: - plasmoid.nativeInterface.connection.resumeAllDevs() - break - default: - plasmoid.nativeInterface.connection.pauseAllDevs() - break - } - } - - Shortcut { - sequence: "Ctrl+Shift+P" - onActivated: connectButton.clicked() - } - } - ToolButton { - id: startStopButton - states: [ - State { - name: "running" - PropertyChanges { - target: startStopButton - visible: true - text: qsTr("Stop") - icon.source: "image://fa/stop" - } - PropertyChanges { - target: startStopToolTip - text: (plasmoid.nativeInterface.service.userScope ? "systemctl --user stop " : "systemctl stop ") - + plasmoid.nativeInterface.service.unitName - } - }, - State { - name: "stopped" - PropertyChanges { - target: startStopButton - visible: true - text: qsTr("Start") - icon.source: "image://fa/play" - } - PropertyChanges { - target: startStopToolTip - text: (plasmoid.nativeInterface.service.userScope ? "systemctl --user start " : "systemctl start ") - + plasmoid.nativeInterface.service.unitName - } - }, - State { - name: "irrelevant" - PropertyChanges { - target: startStopButton - visible: false - } - } - ] - state: { - var nativeInterface = plasmoid.nativeInterface - // the systemd unit status is only relevant when connected to the local instance - if (!nativeInterface.local - || !nativeInterface.startStopEnabled) { - return "irrelevant" - } - // show start/stop button only when the configured unit is available - var service = nativeInterface.service - if (!service || !service.systemdAvailable) { - return "irrelevant" - } - return service.running ? "running" : "stopped" - } - onClicked: plasmoid.nativeInterface.service.toggleRunning() - PlasmaComponents3.ToolTip { - id: startStopToolTip - } - Shortcut { - sequence: "Ctrl+Shift+S" - onActivated: { - if (startStopButton.visible) { - startStopButton.clicked() - } - } - } - } - Item { - Layout.fillWidth: true - } - PlasmaComponents3.ToolButton { - id: showNewNotifications - icon.name: "emblem-warning" - visible: plasmoid.nativeInterface.notificationsAvailable - onClicked: { - plasmoid.nativeInterface.showNotificationsDialog() - plasmoid.expanded = false - } - PlasmaComponents3.ToolTip { - text: qsTr("Show new notifications") - } - Shortcut { - sequence: "Ctrl+N" - onActivated: { - if (showNewNotifications.visible) { - showNewNotifications.clicked() - } - } - } - } - ToolButton { - icon.source: "image://fa/info" - PlasmaComponents3.ToolTip { - text: qsTr("About Syncthing Tray") - } - onClicked: { - plasmoid.nativeInterface.showAboutDialog() - plasmoid.expanded = false - } - } - ToolButton { - id: showOwnIdButton - icon.source: "image://fa/qrcode" - onClicked: { - plasmoid.nativeInterface.showOwnDeviceId() - plasmoid.expanded = false - } - PlasmaComponents3.ToolTip { - text: qsTr("Show own device ID") - } - Shortcut { - sequence: "Ctrl+I" - onActivated: showOwnIdButton.clicked() - } - } - ToolButton { - id: showLogButton - icon.source: "image://fa/file-text" - onClicked: { - plasmoid.nativeInterface.showLog() - plasmoid.expanded = false - } - PlasmaComponents3.ToolTip { - text: qsTr("Show Syncthing log") - } - Shortcut { - sequence: "Ctrl+L" - onActivated: showLogButton.clicked() - } - } - ToolButton { - id: rescanAllDirsButton - icon.source: "image://fa/refresh" - onClicked: plasmoid.nativeInterface.connection.rescanAllDirs() - PlasmaComponents3.ToolTip { - text: qsTr("Rescan all directories") - } - Shortcut { - sequence: "Ctrl+Shift+R" - onActivated: rescanAllDirsButton.clicked() - } - } - ToolButton { - id: settingsButton - icon.source: "image://fa/cog" - onClicked: { - plasmoid.nativeInterface.showSettingsDlg() - plasmoid.expanded = false - } - PlasmaComponents3.ToolTip { - text: qsTr("Settings") - } - Shortcut { - sequence: "Ctrl+S" - onActivated: settingsButton.clicked() - } - } - ToolButton { - id: webUIButton - icon.source: "image://fa/syncthing" - onClicked: { - plasmoid.nativeInterface.showWebUI() - plasmoid.expanded = false - } - PlasmaComponents3.ToolTip { - text: qsTr("Open Syncthing") - } - Shortcut { - sequence: "Ctrl+W" - onActivated: webUIButton.clicked() - } - } - QQ2.ComboBox { - id: connectionConfigsMenu - model: plasmoid.nativeInterface.connectionConfigNames - visible: plasmoid.nativeInterface.connectionConfigNames.length > 1 - currentIndex: plasmoid.nativeInterface.currentConnectionConfigIndex - onCurrentIndexChanged: plasmoid.nativeInterface.currentConnectionConfigIndex = currentIndex - Layout.fillWidth: true - Layout.maximumWidth: implicitWidth - Shortcut { - sequence: "Ctrl+Shift+C" - onActivated: connectionConfigsMenu.popup() - } - } - } - - PlasmaCore.SvgItem { - Layout.preferredWidth: parent.width - Layout.preferredHeight: 2 - elementId: "horizontal-line" - svg: PlasmaCore.Svg { - imagePath: "widgets/line" - } - } - - // global statistics and traffic - GridLayout { - Layout.leftMargin: 5 - Layout.fillWidth: true - Layout.fillHeight: false - columns: 3 - rowSpacing: 1 - columnSpacing: 4 - - Image { - Layout.preferredWidth: 16 - Layout.preferredHeight: 16 - height: 16 - fillMode: Image.PreserveAspectFit - source: "image://fa/globe" - } - StatisticsView { - Layout.leftMargin: 4 - statistics: plasmoid.nativeInterface.globalStatistics - context: qsTr("Global") - } - - IconLabel { - Layout.leftMargin: 10 - iconSource: "image://fa/cloud-download" - iconOpacity: plasmoid.nativeInterface.hasIncomingTraffic ? 1.0 : 0.5 - text: plasmoid.nativeInterface.incomingTraffic - tooltip: qsTr("Global incoming traffic") - } - - Image { - Layout.preferredWidth: 16 - Layout.preferredHeight: 16 - height: 16 - fillMode: Image.PreserveAspectFit - source: "image://fa/home" - } - StatisticsView { - Layout.leftMargin: 4 - statistics: plasmoid.nativeInterface.localStatistics - context: qsTr("Local") - } - - IconLabel { - Layout.leftMargin: 10 - iconSource: "image://fa/cloud-upload" - iconOpacity: plasmoid.nativeInterface.hasOutgoingTraffic ? 1.0 : 0.5 - text: plasmoid.nativeInterface.outgoingTraffic - tooltip: qsTr("Global outgoing traffic") - } - } - - PlasmaCore.SvgItem { - Layout.preferredWidth: parent.width - Layout.preferredHeight: 2 - elementId: "horizontal-line" - svg: PlasmaCore.Svg { - imagePath: "widgets/line" - } - } - - // tab "widget" - RowLayout { - id: tabWidget - spacing: 0 - Layout.minimumWidth: units.gridUnit * plasmoid.nativeInterface.size.width - Layout.minimumHeight: units.gridUnit * plasmoid.nativeInterface.size.height + FocusScope { + anchors.fill: parent + anchors.topMargin: PlasmaCore.Units.smallSpacing * 2 ColumnLayout { - spacing: 0 - Layout.alignment: Qt.AlignTop | Qt.AlignLeft + anchors.fill: parent - PlasmaComponents.TabBar { - id: tabBar - tabPosition: Qt.LeftEdge - Layout.alignment: Qt.AlignTop | Qt.AlignLeft + // ensure keyboard events can be received after initialization + Component.onCompleted: forceActiveFocus() - PlasmaComponents.TabButton { - id: dirsTabButton - iconSource: plasmoid.nativeInterface.loadForkAwesomeIcon("folder") - tab: dirsPage + // define custom key handling for switching tabs, selecting items and filtering + function sendKeyEventToFilter(event) { + var filter = findCurrentFilter() + if (!filter || event.text === "" || filter.activeFocus) { + return } - PlasmaComponents.TabButton { - id: devsTabButton - iconSource: plasmoid.nativeInterface.loadForkAwesomeIcon("sitemap") - tab: devicesPage + if (event.key === Qt.Key_Backspace && filter.text === "") { + filter.explicitelyShown = false + return } - PlasmaComponents.TabButton { - id: downloadsTabButton - iconSource: plasmoid.nativeInterface.loadForkAwesomeIcon("download") - tab: downloadsPage - } - PlasmaComponents.TabButton { - id: recentChangesTabButton - iconSource: plasmoid.nativeInterface.loadForkAwesomeIcon("history") - tab: recentChangesPage + if (event.matches(StandardKey.Paste)) { + filter.paste() + } else { + filter.text = "" + filter.text += event.text } + filter.forceActiveFocus() } - Item { - Layout.fillHeight: true - } - ToolButton { - id: searchButton - Layout.fillWidth: false - Layout.fillHeight: false - Layout.minimumWidth: units.iconSizes.smallMedium - Layout.minimumHeight: units.iconSizes.smallMedium - Layout.alignment: Qt.AlignBottom | Qt.AlignHCenter - icon.source: "image://fa/search" - enabled: mainTabGroup.currentTab === dirsPage - onClicked: { - var filter = findCurrentFilter() - if (!filter) { - return + Keys.onPressed: { + // note: event only received after clicking the tab buttons in plasmoidviewer + // but works as expected in plasmashell + switch (event.key) { + case Qt.Key_Up: + switch (event.modifiers) { + case Qt.NoModifier: + // select previous item in current tab + findCurrentPage().view.decrementCurrentIndex() + break + case Qt.ShiftModifier: + // select previous connection + --plasmoid.nativeInterface.currentConnectionConfigIndex + break } - if (!filter.explicitelyShown) { - filter.explicitelyShown = true - filter.forceActiveFocus() - } else { + break + case Qt.Key_Down: + switch (event.modifiers) { + case Qt.NoModifier: + // select next item in current tab + findCurrentPage().view.incrementCurrentIndex() + break + case Qt.ShiftModifier: + // select previous connection + ++plasmoid.nativeInterface.currentConnectionConfigIndex + break + } + break + case Qt.Key_Left: + // select previous tab + tabBar.currentIndex = tabBar.currentIndex > 0 ? tabBar.currentIndex - 1 : 3 + break + case Qt.Key_Right: + // select next tab + tabBar.currentIndex = tabBar.currentIndex < 3 ? tabBar.currentIndex + 1 : 0 + break + case Qt.Key_Enter: + + // fallthrough + case Qt.Key_Return: + // toggle expanded state of current item + var currentItem = findCurrentPage().view.currentItem + if (currentItem) { + currentItem.expanded = !currentItem.expanded + } + break + case Qt.Key_Escape: + var filter = findCurrentFilter() + if (filter && filter.text !== "") { + // reset filter filter.explicitelyShown = false filter.text = "" + event.accepted = true + } else { + // hide plasmoid + plasmoid.expanded = false + } + break + case Qt.Key_1: + tabBar.currentIndex = 0 + break + case Qt.Key_2: + tabBar.currentIndex = 1 + break + case Qt.Key_3: + tabBar.currentIndex = 2 + break + case Qt.Key_4: + tabBar.currentIndex = 3 + break + default: + sendKeyEventToFilter(event) + return + } + event.accepted = true + } + + // global statistics and traffic + GridLayout { + Layout.leftMargin: 5 + Layout.fillWidth: true + Layout.fillHeight: false + columns: 5 + rowSpacing: 1 + columnSpacing: 4 + + Image { + Layout.preferredWidth: 16 + Layout.preferredHeight: 16 + height: 16 + fillMode: Image.PreserveAspectFit + source: "image://fa/globe" + } + StatisticsView { + Layout.leftMargin: 4 + statistics: plasmoid.nativeInterface.globalStatistics + context: qsTr("Global") + } + IconLabel { + Layout.leftMargin: 10 + iconSource: "image://fa/cloud-download" + iconOpacity: plasmoid.nativeInterface.hasIncomingTraffic ? 1.0 : 0.5 + text: plasmoid.nativeInterface.incomingTraffic + tooltip: qsTr("Global incoming traffic") + } + + Item { + Layout.fillWidth: true + Layout.rowSpan: 2 + } + TinyButton { + id: searchButton + Layout.fillWidth: false + Layout.fillHeight: false + Layout.rowSpan: 2 + icon.source: "image://fa/search" + enabled: tabBar.currentIndex === 0 + opacity: enabled ? 1.0 : 0.25 + tooltip: qsTr("Toggle filter") + onClicked: { + var filter = findCurrentFilter() + if (!filter) { + return + } + if (!filter.explicitelyShown) { + filter.explicitelyShown = true + filter.forceActiveFocus() + } else { + filter.explicitelyShown = false + filter.text = "" + } } } - PlasmaComponents3.ToolTip { - text: qsTr("Toggle filter") + + Image { + Layout.preferredWidth: 16 + Layout.preferredHeight: 16 + height: 16 + fillMode: Image.PreserveAspectFit + source: "image://fa/home" + } + StatisticsView { + Layout.leftMargin: 4 + statistics: plasmoid.nativeInterface.localStatistics + context: qsTr("Local") + } + IconLabel { + Layout.leftMargin: 10 + iconSource: "image://fa/cloud-upload" + iconOpacity: plasmoid.nativeInterface.hasOutgoingTraffic ? 1.0 : 0.5 + text: plasmoid.nativeInterface.outgoingTraffic + tooltip: qsTr("Global outgoing traffic") } } - } - PlasmaCore.SvgItem { - Layout.preferredWidth: 2 - Layout.fillHeight: true - elementId: "vertical-line" - svg: PlasmaCore.Svg { - imagePath: "widgets/line" - } - } - PlasmaComponents.TabGroup { - id: mainTabGroup - currentTab: dirsPage - Layout.fillWidth: true - Layout.fillHeight: true + // tab "widget" + ColumnLayout { + id: tabWidget + spacing: 0 + Layout.fillWidth: true + Layout.fillHeight: true - PlasmaExtras.ConditionalLoader { - id: dirsPage - when: mainTabGroup.currentTab === dirsPage - source: Qt.resolvedUrl("DirectoriesPage.qml") - } - PlasmaExtras.ConditionalLoader { - id: devicesPage - when: mainTabGroup.currentTab === devicesPage - source: Qt.resolvedUrl("DevicesPage.qml") - } - PlasmaExtras.ConditionalLoader { - id: downloadsPage - when: mainTabGroup.currentTab === downloadsPage - source: Qt.resolvedUrl("DownloadsPage.qml") - } - PlasmaExtras.ConditionalLoader { - id: recentChangesPage - when: mainTabGroup.currentTab === recentChangesPage - source: Qt.resolvedUrl("RecentChangesPage.qml") + StackLayout { + id: tabLayout + currentIndex: tabBar.currentIndex + Layout.fillWidth: true + Layout.fillHeight: true + DirectoriesPage { + id: directoriesPage + } + DevicesPage { + id: devicesPage + } + DownloadsPage { + id: downloadsPage + } + RecentChangesPage { + id: recentChangesPage + } + } + PlasmaCore.SvgItem { + Layout.preferredHeight: 1 + Layout.fillWidth: true + elementId: "horizontal-line" + svg: PlasmaCore.Svg { + imagePath: "widgets/line" + } + } + PlasmaComponents3.TabBar { + id: tabBar + position: PlasmaComponents3.TabBar.Footer + spacing: units.smallSpacing + Layout.alignment: Qt.AlignBottom + TabButton { + id: dirsTabButton + text: qsTr("Directories") + icon.source: "image://fa/folder" + } + TabButton { + id: devsTabButton + text: qsTr("Devices") + icon.source: "image://fa/sitemap" + } + TabButton { + id: downloadsTabButton + text: qsTr("Downloads") + icon.source: "image://fa/download" + } + TabButton { + id: recentChangesTabButton + text: qsTr("History") + icon.source: "image://fa/history" + } + } } } } diff --git a/plasmoid/package/contents/ui/TabButton.qml b/plasmoid/package/contents/ui/TabButton.qml new file mode 100644 index 0000000..1cda087 --- /dev/null +++ b/plasmoid/package/contents/ui/TabButton.qml @@ -0,0 +1,29 @@ +import QtQuick 2.8 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 3.0 as PlasmaComponents3 + +PlasmaComponents3.TabButton { + id: root + contentItem: RowLayout { + spacing: label.visible ? units.smallSpacing : 0 + PlasmaCore.ColorScope.inherit: true + Image { + id: image + Layout.preferredHeight: height + source: root.icon.source + height: units.iconSizes.small + fillMode: Image.PreserveAspectFit + } + PlasmaComponents3.Label { + id: label + visible: text.length > 0 + text: root.text + font: root.parent.font + color: PlasmaCore.ColorScope.textColor + elide: Text.ElideRight + Layout.fillWidth: true + Layout.fillHeight: true + } + } +} diff --git a/plasmoid/package/contents/ui/ToolBar.qml b/plasmoid/package/contents/ui/ToolBar.qml new file mode 100644 index 0000000..06a4384 --- /dev/null +++ b/plasmoid/package/contents/ui/ToolBar.qml @@ -0,0 +1,279 @@ +import QtQml 2.3 +import QtQuick 2.7 +import QtQuick.Layouts 1.2 +import org.kde.plasma.components 3.0 as PlasmaComponents3 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.kquickcontrolsaddons 2.0 +import martchus.syncthingplasmoid 0.6 as SyncthingPlasmoid + +RowLayout { + id: toolBar + Layout.fillWidth: true + spacing: PlasmaCore.Units.smallSpacing + Layout.minimumHeight: units.iconSizes.medium + Layout.maximumHeight: units.iconSizes.medium + + readonly property bool showExtraButtons: !(plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentDrawsPlasmoidHeading) + + ToolButton { + id: connectButton + states: [ + State { + name: "disconnected" + PropertyChanges { + target: connectButton + text: qsTr("Connect") + icon.source: "image://fa/refresh" + visible: true + } + }, + State { + name: "connecting" + PropertyChanges { + target: connectButton + visible: false + } + }, + State { + name: "paused" + PropertyChanges { + target: connectButton + text: qsTr("Resume") + icon.source: "image://fa/play" + visible: true + } + }, + State { + name: "idle" + PropertyChanges { + target: connectButton + text: qsTr("Pause") + icon.source: "image://fa/pause" + visible: true + } + } + ] + state: { + switch (plasmoid.nativeInterface.connection.status) { + case SyncthingPlasmoid.Data.Disconnected: + return "disconnected" + case SyncthingPlasmoid.Data.Reconnecting: + return "connecting"; + case SyncthingPlasmoid.Data.Paused: + return "paused" + default: + return "idle" + } + } + onClicked: { + switch (plasmoid.nativeInterface.connection.status) { + case SyncthingPlasmoid.Data.Disconnected: + plasmoid.nativeInterface.connection.connect() + break + case SyncthingPlasmoid.Data.Reconnecting: + break + case SyncthingPlasmoid.Data.Paused: + plasmoid.nativeInterface.connection.resumeAllDevs() + break + default: + plasmoid.nativeInterface.connection.pauseAllDevs() + break + } + } + PlasmaComponents3.ToolTip { + text: connectButton.text + } + Shortcut { + sequence: "Ctrl+Shift+P" + onActivated: connectButton.clicked() + } + } + ToolButton { + id: startStopButton + states: [ + State { + name: "running" + PropertyChanges { + target: startStopButton + visible: true + text: qsTr("Stop") + icon.source: "image://fa/stop" + } + PropertyChanges { + target: startStopToolTip + text: (plasmoid.nativeInterface.service.userScope ? "systemctl --user stop " : "systemctl stop ") + + plasmoid.nativeInterface.service.unitName + } + }, + State { + name: "stopped" + PropertyChanges { + target: startStopButton + visible: true + text: qsTr("Start") + icon.source: "image://fa/play" + } + PropertyChanges { + target: startStopToolTip + text: (plasmoid.nativeInterface.service.userScope ? "systemctl --user start " : "systemctl start ") + + plasmoid.nativeInterface.service.unitName + } + }, + State { + name: "irrelevant" + PropertyChanges { + target: startStopButton + visible: false + } + } + ] + state: { + var nativeInterface = plasmoid.nativeInterface + // the systemd unit status is only relevant when connected to the local instance + if (!nativeInterface.local + || !nativeInterface.startStopEnabled) { + return "irrelevant" + } + // show start/stop button only when the configured unit is available + var service = nativeInterface.service + if (!service || !service.systemdAvailable) { + return "irrelevant" + } + return service.running ? "running" : "stopped" + } + onClicked: plasmoid.nativeInterface.service.toggleRunning() + PlasmaComponents3.ToolTip { + id: startStopToolTip + } + Shortcut { + sequence: "Ctrl+Shift+S" + onActivated: { + if (startStopButton.visible) { + startStopButton.clicked() + } + } + } + } + Item { + Layout.fillWidth: true + } + PlasmaComponents3.ToolButton { + id: showNewNotifications + icon.name: "emblem-warning" + visible: plasmoid.nativeInterface.notificationsAvailable + onClicked: { + plasmoid.nativeInterface.showNotificationsDialog() + plasmoid.expanded = false + } + PlasmaComponents3.ToolTip { + text: qsTr("Show new notifications") + } + Shortcut { + sequence: "Ctrl+N" + onActivated: { + if (showNewNotifications.visible) { + showNewNotifications.clicked() + } + } + } + } + ToolButton { + icon.source: "image://fa/info" + visible: showExtraButtons + onClicked: { + plasmoid.nativeInterface.showAboutDialog() + plasmoid.expanded = false + } + PlasmaComponents3.ToolTip { + text: qsTr("About Syncthing Tray") + } + } + ToolButton { + id: showOwnIdButton + icon.source: "image://fa/qrcode" + visible: showExtraButtons + onClicked: { + plasmoid.nativeInterface.showOwnDeviceId() + plasmoid.expanded = false + } + PlasmaComponents3.ToolTip { + text: qsTr("Show own device ID") + } + Shortcut { + sequence: "Ctrl+I" + onActivated: showOwnIdButton.clicked() + } + } + ToolButton { + id: showLogButton + icon.source: "image://fa/file-text" + visible: showExtraButtons + onClicked: { + plasmoid.nativeInterface.showLog() + plasmoid.expanded = false + } + PlasmaComponents3.ToolTip { + text: qsTr("Show Syncthing log") + } + Shortcut { + sequence: "Ctrl+L" + onActivated: showLogButton.clicked() + } + } + ToolButton { + id: rescanAllDirsButton + icon.source: "image://fa/refresh" + onClicked: plasmoid.nativeInterface.connection.rescanAllDirs() + PlasmaComponents3.ToolTip { + text: qsTr("Rescan all directories") + } + Shortcut { + sequence: "Ctrl+Shift+R" + onActivated: rescanAllDirsButton.clicked() + } + } + ToolButton { + id: settingsButton + icon.source: "image://fa/cog" + visible: showExtraButtons + onClicked: { + plasmoid.nativeInterface.showSettingsDlg() + plasmoid.expanded = false + } + PlasmaComponents3.ToolTip { + text: qsTr("Settings") + } + Shortcut { + sequence: "Ctrl+S" + onActivated: settingsButton.clicked() + } + } + ToolButton { + id: webUIButton + icon.source: "image://fa/syncthing" + onClicked: { + plasmoid.nativeInterface.showWebUI() + plasmoid.expanded = false + } + PlasmaComponents3.ToolTip { + text: qsTr("Open Syncthing") + } + Shortcut { + sequence: "Ctrl+W" + onActivated: webUIButton.clicked() + } + } + PlasmaComponents3.ComboBox { + id: connectionConfigsMenu + model: plasmoid.nativeInterface.connectionConfigNames + visible: plasmoid.nativeInterface.connectionConfigNames.length > 1 + currentIndex: plasmoid.nativeInterface.currentConnectionConfigIndex + onCurrentIndexChanged: plasmoid.nativeInterface.currentConnectionConfigIndex = currentIndex + Layout.fillWidth: true + Layout.maximumWidth: implicitWidth + Shortcut { + sequence: "Ctrl+Shift+C" + onActivated: connectionConfigsMenu.popup() + } + } +} diff --git a/plasmoid/package/contents/ui/main.qml b/plasmoid/package/contents/ui/main.qml index 75868ec..fe1bb75 100644 --- a/plasmoid/package/contents/ui/main.qml +++ b/plasmoid/package/contents/ui/main.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.2 import QtQuick.Layouts 1.1 import org.kde.plasma.plasmoid 2.0 import org.kde.kquickcontrolsaddons 2.0 @@ -6,29 +6,22 @@ import org.kde.kquickcontrolsaddons 2.0 Item { id: syncthingApplet - Plasmoid.switchWidth: units.gridUnit * 25 - Plasmoid.switchHeight: units.gridUnit * 20 - + Plasmoid.switchWidth: units.gridUnit * (plasmoid.nativeInterface.size.width + 1) + Plasmoid.switchHeight: units.gridUnit * (plasmoid.nativeInterface.size.height + 1) Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation Plasmoid.compactRepresentation: CompactRepresentation {} - Plasmoid.fullRepresentation: Loader { - source: "FullRepresentation.qml" - onLoaded: { - if (typeof (item.updateSize) === 'function') { - item.updateSize() - } - } + Plasmoid.fullRepresentation: FullRepresentation { + Layout.minimumWidth: units.gridUnit * plasmoid.nativeInterface.size.width + Layout.minimumHeight: units.gridUnit * plasmoid.nativeInterface.size.height } - Plasmoid.icon: "syncthingtray" Plasmoid.toolTipMainText: plasmoid.nativeInterface.statusText Plasmoid.toolTipSubText: plasmoid.nativeInterface.additionalStatusText - Plasmoid.toolTipItem: Loader { + Plasmoid.toolTipItem: ToolTipView { Layout.minimumWidth: item ? item.width : 0 Layout.maximumWidth: item ? item.width : 0 Layout.minimumHeight: item ? item.height : 0 Layout.maximumHeight: item ? item.height : 0 - source: "ToolTipView.qml" } Plasmoid.hideOnWindowDeactivate: true @@ -37,10 +30,14 @@ Item { plasmoid.nativeInterface.showWebUI() } - function action_showSettings() { + function action_configure() { plasmoid.nativeInterface.showSettingsDlg() } + function action_showOwnId() { + plasmoid.nativeInterface.showOwnDeviceId() + } + function action_rescanAllDirs() { plasmoid.nativeInterface.connection.rescanAllDirs() } @@ -67,13 +64,16 @@ Item { plasmoid.setAction( "showWebUI", qsTr("Open Syncthing"), ":/icons/hicolor/scalable/status/syncthing-default.svg") - plasmoid.setAction("showSettings", qsTr("Settings"), "configure") - plasmoid.setAction("showLog", qsTr("Log"), "text-x-generic") - plasmoid.setAction("showErrors", qsTr("Internal errors"), "data-error") + plasmoid.setAction("configure", qsTr("Settings"), "configure") plasmoid.setAction("rescanAllDirs", qsTr("Rescan all directories"), "folder-sync") + plasmoid.setAction("showOwnId", qsTr("Show own device ID"), + "view-barcode-qr") plasmoid.setAction("restartSyncthing", qsTr("Restart Syncthing"), "system-reboot") + plasmoid.setAction("showLog", qsTr("Log"), "text-x-generic") + plasmoid.setAction("showErrors", qsTr("Internal errors"), "data-error") + plasmoid.action("showErrors").visible = Qt.binding(() => { return plasmoid.nativeInterface.hasInternalErrors }) plasmoid.setAction("showAboutDialog", qsTr("About"), "help-about") } } diff --git a/plasmoid/testing.md b/plasmoid/testing.md index 9aa26e4..2e34ba7 100644 --- a/plasmoid/testing.md +++ b/plasmoid/testing.md @@ -12,6 +12,8 @@ rather than the regular home to separate testing from production. 4. Set `%{sourceDir}/../../syncthingtray/plasmoid/scripts/starttesting.sh plasmoidviewer --applet martchus.syncthingplasmoid` as CLI argument * It is also possible to use `plasmawindowed` or `plasmashell`, see sections below. + * It is also possible to specify `org.kde.plasma.systemtray` as applet to test how the Plasmoid + looks like within the system tray plasmoid. * This usage of `%{sourceDir}` assumes one used the "Building this straight" instructions from the main README.md. 5. Keep `%{buildDir}` as working directory.