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)
This commit is contained in:
Martchus 2021-11-25 20:27:58 +01:00
parent 463f1a0ac6
commit cb1071b3ad
10 changed files with 604 additions and 603 deletions

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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;
};

View File

@ -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)
}
}

View File

@ -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"
}
}
}
}
}

View File

@ -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
}
}
}

View File

@ -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()
}
}
}

View File

@ -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")
}
}

View File

@ -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.