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 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: ; } } } // define shortcuts to trigger actions for currently selected item function clickCurrentItemButton(buttonName) { mainTabGroup.currentTab.item.view.clickCurrentItemButton(buttonName) } Shortcut { sequence: "Ctrl+R" onActivated: clickCurrentItemButton("rescanButton") } Shortcut { sequence: "Ctrl+P" onActivated: clickCurrentItemButton("resumePauseButton") } Shortcut { sequence: "Ctrl+O" 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 PlasmaComponents3.ToolButton { id: connectButton states: [ State { name: "disconnected" PropertyChanges { target: connectButton text: qsTr("Connect") icon.name: "view-refresh" visible: true } }, State { name: "connecting" PropertyChanges { target: connectButton visible: false } }, State { name: "paused" PropertyChanges { target: connectButton text: qsTr("Resume") icon.name: "media-playback-start" visible: true } }, State { name: "idle" PropertyChanges { target: connectButton text: qsTr("Pause") icon.name: "media-playback-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() } } PlasmaComponents3.ToolButton { id: startStopButton states: [ State { name: "running" PropertyChanges { target: startStopButton visible: true text: qsTr("Stop") icon.name: "process-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.name: "system-run" } 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() } } } } PlasmaComponents3.ToolButton { icon.name: "help-about" PlasmaComponents3.ToolTip { text: qsTr("About Syncthing Tray") } onClicked: { plasmoid.nativeInterface.showAboutDialog() plasmoid.expanded = false } } PlasmaComponents3.ToolButton { id: showOwnIdButton // use PlasmaCore.IconItem for the icon because I wouldn't know how to show the QIcon otherwise contentItem: PlasmaCore.IconItem { source: plasmoid.nativeInterface.loadForkAwesomeIcon("qrcode", 64) } // set preferred size so the tool button is displayed consistently with the previous one despite using PlasmaCore.IconItem Layout.preferredWidth: settingsButton.width Layout.preferredHeight: settingsButton.height onClicked: { plasmoid.nativeInterface.showOwnDeviceId() plasmoid.expanded = false } PlasmaComponents3.ToolTip { text: qsTr("Show own device ID") } Shortcut { sequence: "Ctrl+I" onActivated: showOwnIdButton.clicked() } } PlasmaComponents3.ToolButton { id: showLogButton icon.name: "text-x-generic" onClicked: { plasmoid.nativeInterface.showLog() plasmoid.expanded = false } PlasmaComponents3.ToolTip { text: qsTr("Show Syncthing log") } Shortcut { sequence: "Ctrl+L" onActivated: showLogButton.clicked() } } PlasmaComponents3.ToolButton { id: rescanAllDirsButton icon.name: "folder-sync" onClicked: plasmoid.nativeInterface.connection.rescanAllDirs() PlasmaComponents3.ToolTip { text: qsTr("Rescan all directories") } Shortcut { sequence: "Ctrl+Shift+R" onActivated: rescanAllDirsButton.clicked() } } PlasmaComponents3.ToolButton { id: settingsButton icon.name: "preferences-other" onClicked: { plasmoid.nativeInterface.showSettingsDlg() plasmoid.expanded = false } PlasmaComponents3.ToolTip { text: qsTr("Settings") } Shortcut { sequence: "Ctrl+S" onActivated: settingsButton.clicked() } } PlasmaComponents3.ToolButton { id: webUIButton // use PlasmaCore.IconItem for the icon because I wouldn't know how to show the QIcon otherwise contentItem: PlasmaCore.IconItem { source: plasmoid.nativeInterface.syncthingIcon } // set preferred size so the tool button is displayed consistently with the previous one despite using PlasmaCore.IconItem Layout.preferredWidth: settingsButton.width Layout.preferredHeight: settingsButton.height 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 PlasmaCore.IconItem { Layout.preferredWidth: 16 Layout.preferredHeight: 16 source: "globe" } StatisticsView { Layout.leftMargin: 4 statistics: plasmoid.nativeInterface.globalStatistics context: qsTr("Global") } IconLabel { Layout.leftMargin: 10 iconSource: plasmoid.nativeInterface.loadForkAwesomeIcon( "cloud-download") iconOpacity: plasmoid.nativeInterface.hasIncomingTraffic ? 1.0 : 0.5 text: plasmoid.nativeInterface.incomingTraffic tooltip: qsTr("Global incoming traffic") } PlasmaCore.IconItem { Layout.preferredWidth: 16 Layout.preferredHeight: 16 source: "user-home-symbolic" } StatisticsView { Layout.leftMargin: 4 statistics: plasmoid.nativeInterface.localStatistics context: qsTr("Local") } IconLabel { Layout.leftMargin: 10 iconSource: plasmoid.nativeInterface.loadForkAwesomeIcon( "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 ColumnLayout { spacing: 0 Layout.alignment: Qt.AlignTop | Qt.AlignLeft PlasmaComponents.TabBar { id: tabBar tabPosition: Qt.LeftEdge Layout.alignment: Qt.AlignTop | Qt.AlignLeft PlasmaComponents.TabButton { id: dirsTabButton iconSource: "folder-symbolic" tab: dirsPage } PlasmaComponents.TabButton { id: devsTabButton iconSource: "network-server-symbolic" tab: devicesPage } PlasmaComponents.TabButton { id: downloadsTabButton iconSource: "folder-download-symbolic" tab: downloadsPage } PlasmaComponents.TabButton { id: recentChangesTabButton iconSource: "document-open-recent-symbolic" tab: recentChangesPage } } Item { Layout.fillHeight: true } TinyButton { id: searchButton icon.name: "search" enabled: mainTabGroup.currentTab === dirsPage 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 = "" } } } } 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 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") } } } }