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/TopLevelItem.qml
package/contents/ui/DetailView.qml package/contents/ui/DetailView.qml
package/contents/ui/DetailItem.qml package/contents/ui/DetailItem.qml
package/contents/ui/TabButton.qml
package/contents/ui/ToolTipTrigger.qml package/contents/ui/ToolTipTrigger.qml
package/contents/ui/ToolTipView.qml package/contents/ui/ToolTipView.qml
package/contents/ui/ToolBar.qml
package/contents/ui/ToolButton.qml package/contents/ui/ToolButton.qml
package/contents/ui/TinyButton.qml package/contents/ui/TinyButton.qml
package/contents/ui/IconLabel.qml package/contents/ui/IconLabel.qml

View File

@ -15,6 +15,8 @@ X-KDE-ServiceTypes=Plasma/Applet
X-Plasma-NotificationArea=true X-Plasma-NotificationArea=true
X-Plasma-API=declarativeappletscript X-Plasma-API=declarativeappletscript
X-Plasma-MainScript=ui/main.qml X-Plasma-MainScript=ui/main.qml
X-Plasma-NotificationArea=true
X-Plasma-NotificationAreaCategory=SystemServices
X-Plasma-RemoteLocation= X-Plasma-RemoteLocation=
X-KDE-PluginInfo-EnabledByDefault=true X-KDE-PluginInfo-EnabledByDefault=true
X-KDE-PluginInfo-Category=System Information X-KDE-PluginInfo-Category=System Information

View File

@ -65,6 +65,7 @@ SyncthingApplet::SyncthingApplet(QObject *parent, const QVariantList &data)
, m_webViewDlg(nullptr) , m_webViewDlg(nullptr)
#endif #endif
, m_currentConnectionConfig(-1) , m_currentConnectionConfig(-1)
, m_hasInternalErrors(false)
, m_initialized(false) , m_initialized(false)
{ {
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
@ -248,6 +249,11 @@ bool SyncthingApplet::isStartStopEnabled() const
#endif #endif
} }
bool SyncthingApplet::hasInternalErrors() const
{
return m_hasInternalErrors;
}
bool SyncthingApplet::areNotificationsAvailable() const bool SyncthingApplet::areNotificationsAvailable() const
{ {
return !m_notifications.empty(); return !m_notifications.empty();
@ -471,6 +477,9 @@ void SyncthingApplet::handleInternalError(
InternalError error(errorMsg, request.url(), response); InternalError error(errorMsg, request.url(), response);
m_dbusNotifier.showInternalError(error); m_dbusNotifier.showInternalError(error);
InternalErrorsDialog::addError(move(error)); InternalErrorsDialog::addError(move(error));
if (!m_hasInternalErrors) {
emit hasInternalErrorsChanged(m_hasInternalErrors = true);
}
} }
void SyncthingApplet::handleDirStatisticsChanged() void SyncthingApplet::handleDirStatisticsChanged()
@ -481,6 +490,7 @@ void SyncthingApplet::handleDirStatisticsChanged()
void SyncthingApplet::handleErrorsCleared() void SyncthingApplet::handleErrorsCleared()
{ {
emit hasInternalErrorsChanged(m_hasInternalErrors = false);
} }
void SyncthingApplet::handleAboutDialogDeleted() void SyncthingApplet::handleAboutDialogDeleted()

View File

@ -65,6 +65,7 @@ class SyncthingApplet : public Plasma::Applet {
Q_PROPERTY(int currentConnectionConfigIndex READ currentConnectionConfigIndex WRITE setCurrentConnectionConfigIndex NOTIFY Q_PROPERTY(int currentConnectionConfigIndex READ currentConnectionConfigIndex WRITE setCurrentConnectionConfigIndex NOTIFY
currentConnectionConfigIndexChanged) currentConnectionConfigIndexChanged)
Q_PROPERTY(bool startStopEnabled READ isStartStopEnabled NOTIFY settingsChanged) 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(QSize size READ size WRITE setSize NOTIFY sizeChanged)
Q_PROPERTY(bool notificationsAvailable READ areNotificationsAvailable NOTIFY notificationsAvailableChanged) Q_PROPERTY(bool notificationsAvailable READ areNotificationsAvailable NOTIFY notificationsAvailableChanged)
Q_PROPERTY(bool passive READ isPassive NOTIFY passiveChanged) Q_PROPERTY(bool passive READ isPassive NOTIFY passiveChanged)
@ -102,6 +103,7 @@ public:
Data::SyncthingConnectionSettings *connectionConfig(int index); Data::SyncthingConnectionSettings *connectionConfig(int index);
void setCurrentConnectionConfigIndex(int index); void setCurrentConnectionConfigIndex(int index);
bool isStartStopEnabled() const; bool isStartStopEnabled() const;
bool hasInternalErrors() const;
QSize size() const; QSize size() const;
void setSize(const QSize &size); void setSize(const QSize &size);
bool areNotificationsAvailable() const; bool areNotificationsAvailable() const;
@ -147,6 +149,7 @@ Q_SIGNALS:
void statisticsChanged(); void statisticsChanged();
void settingsChanged(); void settingsChanged();
void currentConnectionConfigIndexChanged(int index); void currentConnectionConfigIndexChanged(int index);
void hasInternalErrorsChanged(bool hasInternalErrors);
void sizeChanged(const QSize &size); void sizeChanged(const QSize &size);
void notificationsAvailableChanged(bool notificationsAvailable); void notificationsAvailableChanged(bool notificationsAvailable);
void passiveChanged(bool passive); void passiveChanged(bool passive);
@ -193,6 +196,7 @@ private:
QtGui::WebViewDialog *m_webViewDlg; QtGui::WebViewDialog *m_webViewDlg;
#endif #endif
int m_currentConnectionConfig; int m_currentConnectionConfig;
bool m_hasInternalErrors;
bool m_initialized; bool m_initialized;
QSize m_size; QSize m_size;
}; };

View File

@ -13,6 +13,16 @@ ColumnLayout {
anchors.fill: parent anchors.fill: parent
objectName: "DirectoriesPage" 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 { PlasmaExtras.ScrollArea {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: 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 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.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras 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 org.kde.plasma.components 3.0 as PlasmaComponents3
import martchus.syncthingplasmoid 0.6 as SyncthingPlasmoid
ColumnLayout { PlasmaComponents3.Page {
id: root header: PlasmaExtras.PlasmoidHeading {
ToolBar {
// ensure keyboard events can be received after initialization id: toolbar
Component.onCompleted: forceActiveFocus() width: parent.width
// 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 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 // define shortcuts to trigger actions for currently selected item
function clickCurrentItemButton(buttonName) { function clickCurrentItemButton(buttonName) {
mainTabGroup.currentTab.item.view.clickCurrentItemButton(buttonName) findCurrentPage().view.clickCurrentItemButton(buttonName)
} }
Shortcut { Shortcut {
sequence: "Ctrl+R" sequence: "Ctrl+R"
@ -52,113 +45,17 @@ ColumnLayout {
onActivated: clickCurrentItemButton("openButton") onActivated: clickCurrentItemButton("openButton")
} }
FocusScope {
anchors.fill: parent
anchors.topMargin: PlasmaCore.Units.smallSpacing * 2
ColumnLayout {
anchors.fill: parent
// ensure keyboard events can be received after initialization
Component.onCompleted: forceActiveFocus()
// define custom key handling for switching tabs, selecting items and filtering // 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) { function sendKeyEventToFilter(event) {
var filter = findCurrentFilter() var filter = findCurrentFilter()
if (!filter || event.text === "" || filter.activeFocus) { if (!filter || event.text === "" || filter.activeFocus) {
@ -176,281 +73,81 @@ ColumnLayout {
} }
filter.forceActiveFocus() filter.forceActiveFocus()
} }
Keys.onPressed: {
// heading and right-corner buttons // note: event only received after clicking the tab buttons in plasmoidviewer
RowLayout { // but works as expected in plasmashell
id: toolBar switch (event.key) {
Layout.fillWidth: true case Qt.Key_Up:
Layout.minimumHeight: units.iconSizes.medium switch (event.modifiers) {
Layout.maximumHeight: units.iconSizes.medium case Qt.NoModifier:
// select previous item in current tab
ToolButton { findCurrentPage().view.decrementCurrentIndex()
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 break
case SyncthingPlasmoid.Data.Reconnecting: case Qt.ShiftModifier:
// select previous connection
--plasmoid.nativeInterface.currentConnectionConfigIndex
break break
case SyncthingPlasmoid.Data.Paused: }
plasmoid.nativeInterface.connection.resumeAllDevs() 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 break
default: default:
plasmoid.nativeInterface.connection.pauseAllDevs() sendKeyEventToFilter(event)
break return
}
}
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"
} }
event.accepted = true
} }
// global statistics and traffic // global statistics and traffic
@ -458,7 +155,7 @@ ColumnLayout {
Layout.leftMargin: 5 Layout.leftMargin: 5
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: false Layout.fillHeight: false
columns: 3 columns: 5
rowSpacing: 1 rowSpacing: 1
columnSpacing: 4 columnSpacing: 4
@ -474,7 +171,6 @@ ColumnLayout {
statistics: plasmoid.nativeInterface.globalStatistics statistics: plasmoid.nativeInterface.globalStatistics
context: qsTr("Global") context: qsTr("Global")
} }
IconLabel { IconLabel {
Layout.leftMargin: 10 Layout.leftMargin: 10
iconSource: "image://fa/cloud-download" iconSource: "image://fa/cloud-download"
@ -483,6 +179,34 @@ ColumnLayout {
tooltip: qsTr("Global incoming traffic") 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 = ""
}
}
}
Image { Image {
Layout.preferredWidth: 16 Layout.preferredWidth: 16
Layout.preferredHeight: 16 Layout.preferredHeight: 16
@ -495,7 +219,6 @@ ColumnLayout {
statistics: plasmoid.nativeInterface.localStatistics statistics: plasmoid.nativeInterface.localStatistics
context: qsTr("Local") context: qsTr("Local")
} }
IconLabel { IconLabel {
Layout.leftMargin: 10 Layout.leftMargin: 10
iconSource: "image://fa/cloud-upload" iconSource: "image://fa/cloud-upload"
@ -505,116 +228,65 @@ ColumnLayout {
} }
} }
// tab "widget"
ColumnLayout {
id: tabWidget
spacing: 0
Layout.fillWidth: true
Layout.fillHeight: true
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 { PlasmaCore.SvgItem {
Layout.preferredWidth: parent.width Layout.preferredHeight: 1
Layout.preferredHeight: 2 Layout.fillWidth: true
elementId: "horizontal-line" elementId: "horizontal-line"
svg: PlasmaCore.Svg { svg: PlasmaCore.Svg {
imagePath: "widgets/line" imagePath: "widgets/line"
} }
} }
PlasmaComponents3.TabBar {
// 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 id: tabBar
tabPosition: Qt.LeftEdge position: PlasmaComponents3.TabBar.Footer
Layout.alignment: Qt.AlignTop | Qt.AlignLeft spacing: units.smallSpacing
Layout.alignment: Qt.AlignBottom
PlasmaComponents.TabButton { TabButton {
id: dirsTabButton id: dirsTabButton
iconSource: plasmoid.nativeInterface.loadForkAwesomeIcon("folder") text: qsTr("Directories")
tab: dirsPage icon.source: "image://fa/folder"
} }
PlasmaComponents.TabButton { TabButton {
id: devsTabButton id: devsTabButton
iconSource: plasmoid.nativeInterface.loadForkAwesomeIcon("sitemap") text: qsTr("Devices")
tab: devicesPage icon.source: "image://fa/sitemap"
} }
PlasmaComponents.TabButton { TabButton {
id: downloadsTabButton id: downloadsTabButton
iconSource: plasmoid.nativeInterface.loadForkAwesomeIcon("download") text: qsTr("Downloads")
tab: downloadsPage icon.source: "image://fa/download"
} }
PlasmaComponents.TabButton { TabButton {
id: recentChangesTabButton id: recentChangesTabButton
iconSource: plasmoid.nativeInterface.loadForkAwesomeIcon("history") text: qsTr("History")
tab: recentChangesPage icon.source: "image://fa/history"
} }
} }
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
}
if (!filter.explicitelyShown) {
filter.explicitelyShown = true
filter.forceActiveFocus()
} else {
filter.explicitelyShown = false
filter.text = ""
}
}
PlasmaComponents3.ToolTip {
text: qsTr("Toggle filter")
}
}
}
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")
} }
} }
} }

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 QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.plasmoid 2.0
import org.kde.kquickcontrolsaddons 2.0 import org.kde.kquickcontrolsaddons 2.0
@ -6,29 +6,22 @@ import org.kde.kquickcontrolsaddons 2.0
Item { Item {
id: syncthingApplet id: syncthingApplet
Plasmoid.switchWidth: units.gridUnit * 25 Plasmoid.switchWidth: units.gridUnit * (plasmoid.nativeInterface.size.width + 1)
Plasmoid.switchHeight: units.gridUnit * 20 Plasmoid.switchHeight: units.gridUnit * (plasmoid.nativeInterface.size.height + 1)
Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation
Plasmoid.compactRepresentation: CompactRepresentation {} Plasmoid.compactRepresentation: CompactRepresentation {}
Plasmoid.fullRepresentation: Loader { Plasmoid.fullRepresentation: FullRepresentation {
source: "FullRepresentation.qml" Layout.minimumWidth: units.gridUnit * plasmoid.nativeInterface.size.width
onLoaded: { Layout.minimumHeight: units.gridUnit * plasmoid.nativeInterface.size.height
if (typeof (item.updateSize) === 'function') {
item.updateSize()
} }
}
}
Plasmoid.icon: "syncthingtray" Plasmoid.icon: "syncthingtray"
Plasmoid.toolTipMainText: plasmoid.nativeInterface.statusText Plasmoid.toolTipMainText: plasmoid.nativeInterface.statusText
Plasmoid.toolTipSubText: plasmoid.nativeInterface.additionalStatusText Plasmoid.toolTipSubText: plasmoid.nativeInterface.additionalStatusText
Plasmoid.toolTipItem: Loader { Plasmoid.toolTipItem: ToolTipView {
Layout.minimumWidth: item ? item.width : 0 Layout.minimumWidth: item ? item.width : 0
Layout.maximumWidth: item ? item.width : 0 Layout.maximumWidth: item ? item.width : 0
Layout.minimumHeight: item ? item.height : 0 Layout.minimumHeight: item ? item.height : 0
Layout.maximumHeight: item ? item.height : 0 Layout.maximumHeight: item ? item.height : 0
source: "ToolTipView.qml"
} }
Plasmoid.hideOnWindowDeactivate: true Plasmoid.hideOnWindowDeactivate: true
@ -37,10 +30,14 @@ Item {
plasmoid.nativeInterface.showWebUI() plasmoid.nativeInterface.showWebUI()
} }
function action_showSettings() { function action_configure() {
plasmoid.nativeInterface.showSettingsDlg() plasmoid.nativeInterface.showSettingsDlg()
} }
function action_showOwnId() {
plasmoid.nativeInterface.showOwnDeviceId()
}
function action_rescanAllDirs() { function action_rescanAllDirs() {
plasmoid.nativeInterface.connection.rescanAllDirs() plasmoid.nativeInterface.connection.rescanAllDirs()
} }
@ -67,13 +64,16 @@ Item {
plasmoid.setAction( plasmoid.setAction(
"showWebUI", qsTr("Open Syncthing"), "showWebUI", qsTr("Open Syncthing"),
":/icons/hicolor/scalable/status/syncthing-default.svg") ":/icons/hicolor/scalable/status/syncthing-default.svg")
plasmoid.setAction("showSettings", qsTr("Settings"), "configure") plasmoid.setAction("configure", qsTr("Settings"), "configure")
plasmoid.setAction("showLog", qsTr("Log"), "text-x-generic")
plasmoid.setAction("showErrors", qsTr("Internal errors"), "data-error")
plasmoid.setAction("rescanAllDirs", qsTr("Rescan all directories"), plasmoid.setAction("rescanAllDirs", qsTr("Rescan all directories"),
"folder-sync") "folder-sync")
plasmoid.setAction("showOwnId", qsTr("Show own device ID"),
"view-barcode-qr")
plasmoid.setAction("restartSyncthing", qsTr("Restart Syncthing"), plasmoid.setAction("restartSyncthing", qsTr("Restart Syncthing"),
"system-reboot") "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") 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` 4. Set `%{sourceDir}/../../syncthingtray/plasmoid/scripts/starttesting.sh plasmoidviewer --applet martchus.syncthingplasmoid`
as CLI argument as CLI argument
* It is also possible to use `plasmawindowed` or `plasmashell`, see sections below. * 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 * This usage of `%{sourceDir}` assumes one used the "Building this straight" instructions
from the main README.md. from the main README.md.
5. Keep `%{buildDir}` as working directory. 5. Keep `%{buildDir}` as working directory.