Add Plasmoid (still experimental and incomplete)

This commit is contained in:
Martchus 2017-08-29 23:58:49 +02:00
parent c8b93fc472
commit f9b6441a8e
20 changed files with 1162 additions and 26 deletions

3
.gitignore vendored
View File

@ -44,7 +44,6 @@ Makefile*
testfiles/output.*
# experimental
plasmoid/
scripts/
# clang-format
@ -54,5 +53,5 @@ scripts/
/model/.clang-format
/testhelper/.clang-format
/tray/.clang-format
/plasmoid/.clang-format
/plasmoid/lib/.clang-format
/widgets/.clang-format

View File

@ -17,8 +17,11 @@ project(${META_PROJECT_NAME})
# disable Dolphin integration under Android, Windows and MacOS by default
if(ANDROID OR WIN32 OR APPLE)
set(FILE_ITEM_ACTION_PLUGIN_DISABLED_BY_DEFAULT ON)
set(PLASMOID_DISABLED_BY_DEFAULT ON)
else()
set(FILE_ITEM_ACTION_PLUGIN_DISABLED_BY_DEFAULT OFF)
# plasmoid is still incomplete, so disable it by default
set(PLASMOID_DISABLED_BY_DEFAULT ON)
endif()
# options for partial build
@ -27,6 +30,7 @@ option(NO_TRAY "whether building the tray should be skipped" OFF)
option(NO_FILE_ITEM_ACTION_PLUGIN "whether building the file item action plugin should be skipped" "${FILE_ITEM_ACTION_PLUGIN_DISABLED_BY_DEFAULT}")
option(NO_MODEL "whether building models should be skipped, implies NO_TRAY" OFF)
option(NO_WIDGETS "whether building widgets should be skipped, implies NO_TRAY" OFF)
option(NO_PLASMOID "whether building the Plasma 5 plasmoid should be skipped" "${PLASMOID_DISABLED_BY_DEFAULT}")
# add subdirectories
enable_testing()
@ -45,6 +49,9 @@ if(NOT NO_MODEL)
if(NOT NO_TRAY)
add_subdirectory(tray)
endif()
if(NOT NO_PLASMOID)
add_subdirectory(plasmoid)
endif()
endif()
endif()
if(NOT NO_FILE_ITEM_ACTION_PLUGIN)

View File

@ -6,11 +6,11 @@ Qt 5-based tray application for [Syncthing](https://github.com/syncthing/syncthi
support
* No desktop environment specific libraries required
* Tested under \*
* Plasma 5
* Openbox/qt5ct/Tint2
* Awesome/qt5ct
* Cinnamon
* Windows 10
* Plasma 5
* Openbox/qt5ct/Tint2
* Awesome/qt5ct
* Cinnamon
* Windows 10
* Can be shown as regular window if tray icon support is not available
\* If you can confirm it works under other desktop environments, please add it
@ -18,16 +18,16 @@ to the list. Maybe someone could check whether it works under Mac OS X.
## Features
* Provides quick access to most frequently used features but does not intend to replace the official web UI
* Check state of directories and devices
* Check current traffic statistics
* Display further details about directories and devices, like last file, last
scan, items out of sync, ...
* Display ongoing downloads
* Display Syncthing log
* Trigger re-scan of a specific directory or all directories at once
* Open a directory with the default file browser
* Pause/resume a specific device or all devices at once
* Pause/resume a specific directory
* Check state of directories and devices
* Check current traffic statistics
* Display further details about directories and devices, like last file, last
scan, items out of sync, ...
* Display ongoing downloads
* Display Syncthing log
* Trigger re-scan of a specific directory or all directories at once
* Open a directory with the default file browser
* Pause/resume a specific device or all devices at once
* Pause/resume a specific directory
* Shows Syncthing notifications
* Can read the local Syncthing configuration file for quick setup when just connecting to local instance
* Can show the status of the Syncthing systemd unit and allows to start and stop it (see section *Use of systemd*)
@ -35,25 +35,26 @@ to the list. Maybe someone could check whether it works under Mac OS X.
* Can launch Syncthing and syncthing-inotify automatically when started and display stdout/stderr (useful under
Windows)
* Provides quick access to the official web UI
* Utilizes either Qt WebKit or Qt WebEngine
* Can be built without web view support as well (then the web UI is opened in the regular browser)
* Utilizes either Qt WebKit or Qt WebEngine
* Can be built without web view support as well (then the web UI is opened in the regular browser)
* Allows quickly switching between multiple Syncthing instances
* Shows notifications via Qt or uses D-Bus notification daemon directly
* Also features a simple command line utility `syncthingctl` to check Syncthing status and trigger
rescan/pause/resume/restart
* Also bundles a KIO plugin which shows the status of a Syncthing directory
and allows to trigger Syncthing actions in Dolphin file manager
* Rescan selected items
* Rescan entire Syncthing directory
* Pause/resume Syncthing directory
* See also screenshots section
* Rescan selected items
* Rescan entire Syncthing directory
* Pause/resume Syncthing directory
* See also screenshots section
* English and German localization
## Planned features
The tray is still under development; the following features are planned:
* Show recently processed items
* Improve notification handling
* Create Plasmoid for Plasma 5 desktop
* An incomplete/experimental version is already available
* Provide built-in support for file system watches
## Screenshots
@ -135,7 +136,6 @@ to the CMake arguments. Then only core and network are required.
#### Building this straight
0. Install (preferably the latest version of) g++ or clang, the required Qt 5 modules and CMake.
1. Get the sources. For the lastest version from Git clone the following repositories:
```
cd $SOURCES
git clone https://github.com/Martchus/cpp-utilities.git c++utilities
@ -144,7 +144,6 @@ to the CMake arguments. Then only core and network are required.
git clone https://github.com/Martchus/subdirs.git
```
2. Build and install everything in one step:
```
cd $BUILD_DIR
cmake \

27
plasmoid/CMakeLists.txt Normal file
View File

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 2.8.12)
# meta data
set(META_PROJECT_NAME syncthingplasmoid)
set(META_APP_NAME "Syncthing Plasmoid")
set(META_APP_AUTHOR "Martchus")
set(META_APP_DESCRIPTION "Plasmoid to interact with Syncthing")
set(META_PROJECT_TYPE qtplugin)
set(META_ID "martchus.syncthing-plasmoid")
# find ECM (required by KF5Plasma)
find_package(ECM REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_MODULE_PATH})
# find KF5Plasma
find_package(KF5Plasma REQUIRED)
# add subdirs
add_subdirectory(lib)
plasma_install_package(package ${META_ID})
set(PLASMOID_TESTDIR "${CMAKE_CURRENT_BINARY_DIR}/testdir" CACHE STRING "specifies the Plasmoid test directory")
file(MAKE_DIRECTORY "${PLASMOID_TESTDIR}")
add_custom_target(init_plasmoid_testing
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/scripts/inittesting.sh"
WORKING_DIRECTORY "${PLASMOID_TESTDIR}"
)

View File

@ -0,0 +1,51 @@
# source files
set(HEADER_FILES
syncthingapplet.h
)
set(SRC_FILES
syncthingapplet.cpp
)
set(PLASMOID_FILES
../package/metadata.desktop
../package/contents/ui/CompactRepresentation.qml
../package/contents/ui/FullRepresentation.qml
../package/contents/ui/DirectoriesPage.qml
../package/contents/ui/DevicesPage.qml
../package/contents/ui/DownloadsPage.qml
../package/contents/ui/TopLevelView.qml
../package/contents/ui/TopLevelItem.qml
../package/contents/ui/DetailView.qml
../package/contents/ui/DetailItem.qml
../package/contents/ui/main.qml
)
list(APPEND QML_FILES ${PLASMOID_FILES})
# find c++utilities
find_package(c++utilities 4.6.0 REQUIRED)
list(APPEND CMAKE_MODULE_PATH ${CPP_UTILITIES_MODULE_DIRS})
# find qtutilities
find_package(qtutilities 5.0.0 REQUIRED)
use_qt_utilities()
# find backend libraries
find_package(syncthingconnector ${META_APP_VERSION} REQUIRED)
use_syncthingconnector()
find_package(syncthingmodel ${META_APP_VERSION} REQUIRED)
use_syncthingmodel()
find_package(syncthingwidgets ${META_APP_VERSION} REQUIRED)
use_syncthingwidgets()
# link also explicitely against the following Qt 5 modules
list(APPEND ADDITIONAL_QT_MODULES Network Qml)
list(APPEND ADDITIONAL_KF_MODULES Plasma)
include(BasicConfig)
include(QtGuiConfig)
include(QtConfig)
include(WindowsResources)
include(LibraryTarget)
include(ConfigHeader)
# what ever this does, it is done
kcoreaddons_desktop_to_json("${META_PROJECT_NAME}" ../package/metadata.desktop)

27
plasmoid/lib/global.h Normal file
View File

@ -0,0 +1,27 @@
// Created via CMake from template global.h.in
// WARNING! Any changes to this file will be overwritten by the next CMake run!
#ifndef SYNCTHINGPLASMOID_GLOBAL
#define SYNCTHINGPLASMOID_GLOBAL
#include <c++utilities/application/global.h>
#ifdef SYNCTHINGPLASMOID_STATIC
#define SYNCTHINGPLASMOID_EXPORT
#define SYNCTHINGPLASMOID_IMPORT
#else
#define SYNCTHINGPLASMOID_EXPORT LIB_EXPORT
#define SYNCTHINGPLASMOID_IMPORT LIB_IMPORT
#endif
/*!
* \def SYNCTHINGPLASMOID_EXPORT
* \brief Marks the symbol to be exported by the syncthingplasmoid library.
*/
/*!
* \def SYNCTHINGPLASMOID_IMPORT
* \brief Marks the symbol to be imported from the syncthingplasmoid library.
*/
#endif // SYNCTHINGPLASMOID_GLOBAL

View File

@ -0,0 +1,194 @@
#include "./syncthingapplet.h"
#include "../../widgets/misc/otherdialogs.h"
#include "../../widgets/misc/textviewdialog.h"
#include "../../widgets/settings/settings.h"
#include "../../widgets/settings/settingsdialog.h"
#include "../../widgets/webview/webviewdialog.h"
#include "../../model/syncthingicons.h"
#include "../../connector/utils.h"
#include "resources/config.h"
#include <qtutilities/misc/desktoputils.h>
#include <qtutilities/misc/dialogutils.h>
#include <qtutilities/resources/resources.h>
#include <qtutilities/settingsdialog/optioncategory.h>
#include <qtutilities/settingsdialog/settingsdialog.h>
#include <QDesktopServices>
#include <QQmlEngine>
#include <iostream>
using namespace std;
using namespace Data;
using namespace Plasma;
using namespace Dialogs;
using namespace QtGui;
SyncthingApplet::SyncthingApplet(QObject *parent, const QVariantList &data)
: Applet(parent, data)
, m_aboutDlg(nullptr)
, m_connection()
, m_dirModel(m_connection)
, m_devModel(m_connection)
, m_downloadModel(m_connection)
, m_connectionSettingsDlg(nullptr)
#ifndef SYNCTHINGWIDGETS_NO_WEBVIEW
, m_webViewDlg(nullptr)
#endif
{
qRegisterMetaType<Data::SyncthingStatus>("Data::SyncthingStatus");
qmlRegisterUncreatableMetaObject(Data::staticMetaObject, "martchus.syncthingplasmoid", 0, 6, "Data", QStringLiteral("only enums"));
}
SyncthingApplet::~SyncthingApplet()
{
delete m_connectionSettingsDlg;
#ifndef SYNCTHINGWIDGETS_NO_WEBVIEW
delete m_webViewDlg;
#endif
}
QIcon SyncthingApplet::statusIcon() const
{
return m_statusInfo.statusIcon();
}
QString SyncthingApplet::incomingTraffic() const
{
return trafficString(m_connection.totalIncomingTraffic(), m_connection.totalIncomingRate());
}
QString SyncthingApplet::outgoingTraffic() const
{
return trafficString(m_connection.totalOutgoingTraffic(), m_connection.totalOutgoingRate());
}
QString SyncthingApplet::statusText() const
{
return m_statusInfo.statusText();
}
QString SyncthingApplet::additionalStatusText() const
{
return m_statusInfo.additionalStatusText();
}
void SyncthingApplet::init()
{
LOAD_QT_TRANSLATIONS;
Applet::init();
connect(&m_connection, &SyncthingConnection::statusChanged, this, &SyncthingApplet::handleConnectionStatusChanged);
connect(&m_connection, &SyncthingConnection::trafficChanged, this, &SyncthingApplet::trafficChanged);
// restore settings
Settings::restore();
applyConnectionSettings();
}
void SyncthingApplet::showConnectionSettingsDlg()
{
if (!m_connectionSettingsDlg) {
m_connectionSettingsDlg = new Dialogs::SettingsDialog;
m_connectionSettingsDlg->setTabBarAlwaysVisible(false);
auto *const webViewPage = new QtGui::WebViewOptionPage;
auto *const webViewWidget = webViewPage->widget();
webViewWidget->setWindowTitle(tr("Web view"));
webViewWidget->setWindowIcon(QIcon::fromTheme(QStringLiteral("internet-web-browser")));
auto *const category = new Dialogs::OptionCategory(m_connectionSettingsDlg);
category->assignPages({ new QtGui::ConnectionOptionPage(&m_connection), webViewPage });
m_connectionSettingsDlg->setSingleCategory(category);
m_connectionSettingsDlg->resize(860, 620);
connect(m_connectionSettingsDlg, &Dialogs::SettingsDialog::applied, this, &SyncthingApplet::applyConnectionSettings);
connect(m_connectionSettingsDlg, &Dialogs::SettingsDialog::applied, &Settings::save);
}
Dialogs::centerWidget(m_connectionSettingsDlg);
m_connectionSettingsDlg->show();
m_connectionSettingsDlg->activateWindow();
}
void SyncthingApplet::showWebUI()
{
#ifndef SYNCTHINGWIDGETS_NO_WEBVIEW
if (Settings::values().webView.disabled) {
#endif
QDesktopServices::openUrl(m_connection.syncthingUrl());
#ifndef SYNCTHINGWIDGETS_NO_WEBVIEW
} else {
if (!m_webViewDlg) {
m_webViewDlg = new WebViewDialog;
//if(m_selectedConnection) {
m_webViewDlg->applySettings(Settings::values().connection.primary);
//}
connect(m_webViewDlg, &WebViewDialog::destroyed, this, &SyncthingApplet::handleWebViewDeleted);
}
m_webViewDlg->show();
m_webViewDlg->activateWindow();
}
#endif
}
void SyncthingApplet::showLog()
{
auto *const dlg = TextViewDialog::forLogEntries(m_connection);
dlg->setAttribute(Qt::WA_DeleteOnClose, true);
centerWidget(dlg);
dlg->show();
}
void SyncthingApplet::showOwnDeviceId()
{
auto *const dlg = ownDeviceIdDialog(m_connection);
dlg->setAttribute(Qt::WA_DeleteOnClose, true);
centerWidget(dlg);
dlg->show();
}
void SyncthingApplet::showAboutDialog()
{
if (!m_aboutDlg) {
m_aboutDlg = new AboutDialog(nullptr, QString(), QStringLiteral(APP_AUTHOR "\nSyncthing icons from Syncthing project"), QString(), QString(),
QStringLiteral(APP_DESCRIPTION), QImage(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg")));
m_aboutDlg->setWindowTitle(tr("About") + QStringLiteral(" - " APP_NAME));
m_aboutDlg->setWindowIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg")));
m_aboutDlg->setAttribute(Qt::WA_DeleteOnClose);
connect(m_aboutDlg, &QObject::destroyed, this, &SyncthingApplet::handleAboutDialogDeleted);
}
centerWidget(m_aboutDlg);
m_aboutDlg->show();
m_aboutDlg->activateWindow();
}
void SyncthingApplet::applyConnectionSettings()
{
m_connection.connect(Settings::values().connection.primary);
if (m_webViewDlg) {
m_webViewDlg->applySettings(Settings::values().connection.primary);
}
}
void SyncthingApplet::handleConnectionStatusChanged()
{
m_statusInfo.update(m_connection);
emit connectionStatusChanged();
}
void SyncthingApplet::handleAboutDialogDeleted()
{
m_aboutDlg = nullptr;
}
void SyncthingApplet::handleWebViewDeleted()
{
m_webViewDlg = nullptr;
}
K_EXPORT_PLASMA_APPLET_WITH_JSON(syncthing, SyncthingApplet, "metadata.json")
#include "syncthingapplet.moc"

View File

@ -0,0 +1,119 @@
#ifndef SYNCTHINGAPPLET_H
#define SYNCTHINGAPPLET_H
#include "../../widgets/misc/statusinfo.h"
#include "../../model/syncthingdevicemodel.h"
#include "../../model/syncthingdirectorymodel.h"
#include "../../model/syncthingdownloadmodel.h"
#include "../../connector/syncthingconnection.h"
#include <qtutilities/aboutdialog/aboutdialog.h>
#include <Plasma/Applet>
namespace Dialogs {
class SettingsDialog;
}
namespace Data {
class SyncthingConnection;
class SyncthingDirectoryModel;
class SyncthingDeviceModel;
class SyncthingDownloadModel;
}
namespace QtGui {
class WebViewDialog;
}
class SyncthingApplet : public Plasma::Applet {
Q_OBJECT
Q_PROPERTY(Data::SyncthingConnection *connection READ connection NOTIFY connectionChanged)
Q_PROPERTY(Data::SyncthingDirectoryModel *dirModel READ dirModel NOTIFY dirModelChanged)
Q_PROPERTY(Data::SyncthingDeviceModel *devModel READ devModel NOTIFY devModelChanged)
Q_PROPERTY(Data::SyncthingDownloadModel *downloadModel READ downloadModel NOTIFY downloadModelChanged)
Q_PROPERTY(QString statusText READ statusText NOTIFY connectionStatusChanged)
Q_PROPERTY(QString additionalStatusText READ additionalStatusText NOTIFY connectionStatusChanged)
Q_PROPERTY(QIcon statusIcon READ statusIcon NOTIFY connectionStatusChanged)
Q_PROPERTY(QString incomingTraffic READ incomingTraffic NOTIFY trafficChanged)
Q_PROPERTY(QString outgoingTraffic READ outgoingTraffic NOTIFY trafficChanged)
public:
SyncthingApplet(QObject *parent, const QVariantList &data);
~SyncthingApplet() override;
public:
Data::SyncthingConnection *connection() const;
Data::SyncthingDirectoryModel *dirModel() const;
Data::SyncthingDeviceModel *devModel() const;
Data::SyncthingDownloadModel *downloadModel() const;
QString statusText() const;
QString additionalStatusText() const;
QIcon statusIcon() const;
QString incomingTraffic() const;
QString outgoingTraffic() const;
public Q_SLOTS:
void init() Q_DECL_OVERRIDE;
void showConnectionSettingsDlg();
void showWebUI();
void showLog();
void showOwnDeviceId();
void showAboutDialog();
Q_SIGNALS:
/// \remarks Never emitted, just to silence "... depends on non-NOTIFYable ..."
void connectionChanged();
/// \remarks Never emitted, just to silence "... depends on non-NOTIFYable ..."
void dirModelChanged();
/// \remarks Never emitted, just to silence "... depends on non-NOTIFYable ..."
void devModelChanged();
/// \remarks Never emitted, just to silence "... depends on non-NOTIFYable ..."
void downloadModelChanged();
void connectionStatusChanged();
void trafficChanged();
private Q_SLOTS:
void applyConnectionSettings();
void handleConnectionStatusChanged();
void handleAboutDialogDeleted();
#ifndef SYNCTHINGWIDGETS_NO_WEBVIEW
void handleWebViewDeleted();
#endif
private:
Dialogs::AboutDialog *m_aboutDlg;
Data::SyncthingConnection m_connection;
QtGui::StatusInfo m_statusInfo;
Data::SyncthingDirectoryModel m_dirModel;
Data::SyncthingDeviceModel m_devModel;
Data::SyncthingDownloadModel m_downloadModel;
Dialogs::SettingsDialog *m_connectionSettingsDlg;
#ifndef SYNCTHINGWIDGETS_NO_WEBVIEW
QtGui::WebViewDialog *m_webViewDlg;
#endif
};
inline Data::SyncthingConnection *SyncthingApplet::connection() const
{
return const_cast<Data::SyncthingConnection *>(&m_connection);
}
inline Data::SyncthingDirectoryModel *SyncthingApplet::dirModel() const
{
return const_cast<Data::SyncthingDirectoryModel *>(&m_dirModel);
}
inline Data::SyncthingDeviceModel *SyncthingApplet::devModel() const
{
return const_cast<Data::SyncthingDeviceModel *>(&m_devModel);
}
inline Data::SyncthingDownloadModel *SyncthingApplet::downloadModel() const
{
return const_cast<Data::SyncthingDownloadModel *>(&m_downloadModel);
}
#endif // SYNCTHINGAPPLET_H

View File

@ -0,0 +1,18 @@
import QtQuick 2.0
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import martchus.syncthingplasmoid 0.6 as SyncthingPlasmoid
MouseArea {
Layout.fillWidth: false
hoverEnabled: true
onClicked: plasmoid.expanded = !plasmoid.expanded
PlasmaCore.IconItem {
id: icon
anchors.fill: parent
source: plasmoid.nativeInterface.statusIcon
active: parent.containsMouse
}
}

View File

@ -0,0 +1,27 @@
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
RowLayout {
Item {
width: 21
}
PlasmaComponents.Label {
Layout.preferredWidth: 100
text: name
font.pointSize: 8
height: contentHeight
elide: Text.ElideRight
}
PlasmaComponents.Label {
Layout.fillWidth: true
text: detail
font.pointSize: 8
height: contentHeight
elide: Text.ElideRight
}
}

View File

@ -0,0 +1,13 @@
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
ListView {
currentIndex: -1
interactive: false
height: contentHeight
}

View File

@ -0,0 +1,86 @@
import QtQuick 2.3
import QtQuick.Layouts 1.1
import QtQml.Models 2.2
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
Item {
property alias view: deviceView
anchors.fill: parent
objectName: "DevicesPage"
PlasmaExtras.ScrollArea {
anchors.fill: parent
TopLevelView {
id: deviceView
model: plasmoid.nativeInterface.devModel
delegate: TopLevelItem {
id: item
ColumnLayout {
width: parent.width
spacing: 0
RowLayout {
RowLayout {
spacing: 5
PlasmaCore.IconItem {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
anchors.verticalCenter: parent.verticalCenter
source: statusIcon
}
PlasmaComponents.Label {
anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight
text: name
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
RowLayout {
id: toolButtonsLayout
spacing: 0
PlasmaComponents.Label {
height: implicitHeight
text: statusString
color: statusColor
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: 3
}
PlasmaComponents.ToolButton {
id: barcodeToolButton
iconSource: paused ? "media-playback-start" : "media-playback-pause";
tooltip: paused ? qsTr("Resume") : qsTr("Pause")
onClicked: paused
? plasmoid.nativeInterface.connection.resumeDevice([devId])
: plasmoid.nativeInterface.connection.pauseDevice([devId])
}
}
}
DetailView {
id: detailsView
visible: item.expanded
model: DelegateModel {
model: plasmoid.nativeInterface.devModel
rootIndex: detailsView.model.modelIndex(index)
delegate: DetailItem { }
}
}
}
}
}
}
}

View File

@ -0,0 +1,100 @@
import QtQuick 2.3
import QtQuick.Layouts 1.1
import QtQml.Models 2.2
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
Item {
property alias view: directoryView
anchors.fill: parent
objectName: "DirectoriesPage"
PlasmaExtras.ScrollArea {
anchors.fill: parent
TopLevelView {
id: directoryView
model: plasmoid.nativeInterface.dirModel
delegate: TopLevelItem {
id: item
ColumnLayout {
width: parent.width
spacing: 0
RowLayout {
id: itemSummary
RowLayout {
spacing: 5
PlasmaCore.IconItem {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
anchors.verticalCenter: parent.verticalCenter
source: statusIcon
}
PlasmaComponents.Label {
anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight
text: name
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
RowLayout {
id: toolButtonsLayout
spacing: 0
PlasmaComponents.Label {
height: implicitHeight
text: statusString
color: statusColor
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: 3
}
PlasmaComponents.ToolButton {
iconSource: "view-refresh"
tooltip: qsTr("Rescan")
onClicked: plasmoid.nativeInterface.connection.rescan(dirId)
}
PlasmaComponents.ToolButton {
id: barcodeToolButton
iconSource: paused ? "media-playback-start" : "media-playback-pause";
tooltip: paused ? qsTr("Resume") : qsTr("Pause")
onClicked: paused
? plasmoid.nativeInterface.connection.resumeDirectories([dirId])
: plasmoid.nativeInterface.connection.pauseDirectories([dirId])
}
PlasmaComponents.ToolButton {
iconSource: "folder"
tooltip: qsTr("Open in file browser")
onClicked: {
Qt.openUrlExternally(path)
plasmoid.expanded = false
}
}
}
}
DetailView {
id: detailsView
visible: item.expanded
model: DelegateModel {
model: plasmoid.nativeInterface.dirModel
rootIndex: detailsView.model.modelIndex(index)
delegate: DetailItem { }
}
}
}
}
}
}
}

View File

@ -0,0 +1,39 @@
import QtQuick 2.3
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
Item {
property alias view: downloadView
anchors.fill: parent
objectName: "DownloadsPage"
PlasmaExtras.ScrollArea {
anchors.fill: parent
PlasmaComponents.Label {
text: "TODO: download delegate/model"
}
TopLevelView {
id: downloadView
model: plasmoid.nativeInterface.downloadModel
delegate: TopLevelItem {
RowLayout {
PlasmaCore.IconItem {
id: listIcon
source: fileIcon
}
PlasmaComponents.Label {
height: implicitHeight
elide: Text.ElideRight
text: name
}
}
}
}
}
}

View File

@ -0,0 +1,287 @@
import QtQuick 2.3
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
import martchus.syncthingplasmoid 0.6 as SyncthingPlasmoid
ColumnLayout {
id: root
Layout.minimumWidth: units.gridUnit * 26
Layout.minimumHeight: units.gridUnit * 34
Keys.onPressed: {
// FIXME: currently only works after clicking the tab buttons
// TODO: add more shortcuts
switch(event.key) {
case Qt.Key_Up:
mainTabGroup.currentTab.item.view.decrementCurrentIndex();
event.accepted = true;
break;
case Qt.Key_Down:
mainTabGroup.currentTab.item.view.incrementCurrentIndex();
event.accepted = true;
break;
case Qt.Key_Enter:
case Qt.Key_Return:
var currentItem = mainTabGroup.currentTab.item.view.currentItem;
if (currentItem) {
currentItem.expanded = !currentItem.expanded
}
break;
case Qt.Key_Escape:
break;
default:
break;
}
}
// heading and right-corner buttons
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: false
PlasmaExtras.Heading {
id: headingLabel
level: 1
text: qsTr("Syncthing")
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
RowLayout {
id: toolBar
anchors.verticalCenter: parent.verticalCenter
PlasmaComponents.ToolButton {
id: connectButton
states: [
State {
name: "disconnected"
PropertyChanges {
target: connectButton
text: qsTr("Connect")
iconSource: "view-refresh"
}
},
State {
name: "paused"
PropertyChanges {
target: connectButton
text: qsTr("Resume")
iconSource: "media-playback-start"
}
},
State {
name: "idle"
PropertyChanges {
target: connectButton
text: qsTr("Pause")
iconSource: "media-playback-pause"
}
}
]
state: {
switch(plasmoid.nativeInterface.connection.status) {
case SyncthingPlasmoid.Data.Disconnected:
case SyncthingPlasmoid.Data.Reconnecting:
return "disconnected";
case SyncthingPlasmoid.Data.Paused:
return "paused";
default:
return "idle";
}
}
tooltip: text
onClicked: function() {
switch(plasmoid.nativeInterface.connection.status) {
case SyncthingPlasmoid.Data.Disconnected:
case SyncthingPlasmoid.Data.Reconnecting:
plasmoid.nativeInterface.connection.connect();
break;
case SyncthingPlasmoid.Data.Paused:
plasmoid.nativeInterface.connection.resumeAllDevs();
break;
default:
plasmoid.nativeInterface.connection.pauseAllDevs();
break;
}
}
}
PlasmaComponents.ToolButton {
id: startStopButton
text: qsTr("TODO: Start/stop")
tooltip: text
iconSource: "system-run"
onClicked: plasmoid.nativeInterface.toggleSyncthingRunning()
}
PlasmaComponents.ToolButton {
tooltip: qsTr("Show own device ID")
iconSource: "view-barcode"
onClicked: {
plasmoid.nativeInterface.showOwnDeviceId()
plasmoid.expanded = false
}
}
PlasmaComponents.ToolButton {
tooltip: qsTr("Show Syncthing log")
iconSource: "text-x-generic"
onClicked: {
plasmoid.nativeInterface.showLog()
plasmoid.expanded = false
}
}
PlasmaComponents.ToolButton {
tooltip: qsTr("Rescan all directories")
iconSource: "view-refresh"
onClicked: plasmoid.nativeInterface.connection.rescanAllDirs()
}
PlasmaComponents.ToolButton {
tooltip: qsTr("Settings")
iconSource: "preferences-other"
onClicked: {
plasmoid.nativeInterface.showConnectionSettingsDlg()
plasmoid.expanded = false
}
}
PlasmaComponents.ToolButton {
tooltip: qsTr("Web UI")
iconSource: "internet-web-browser"
onClicked: {
plasmoid.nativeInterface.showWebUI();
plasmoid.expanded = false
}
}
}
}
PlasmaCore.SvgItem {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 2
elementId: "horizontal-line"
svg: PlasmaCore.Svg {
imagePath: "widgets/line"
}
}
// traffic and connection selection
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: false
PlasmaCore.IconItem {
source: "network-card"
Layout.preferredWidth: 32
Layout.preferredHeight: 32
}
ColumnLayout {
Layout.fillHeight: true
spacing: 1
PlasmaComponents.Label {
text: qsTr("In")
}
PlasmaComponents.Label {
text: qsTr("Out")
}
}
ColumnLayout {
Layout.fillHeight: true
spacing: 1
PlasmaComponents.Label {
text: plasmoid.nativeInterface.incomingTraffic;
}
PlasmaComponents.Label {
text: plasmoid.nativeInterface.outgoingTraffic;
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
ToolButton {
text: qsTr("TODO: connection selection")
iconSource: "network-connect"
// FIXME: figure out why menu doesn't work in plasmoidviewer
menu: Menu {
MenuItem {
text: qsTr("foo")
}
MenuItem {
text: qsTr("bar")
}
}
}
}
PlasmaCore.SvgItem {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 2
elementId: "horizontal-line"
svg: PlasmaCore.Svg {
imagePath: "widgets/line"
}
}
// tab "widget"
RowLayout {
spacing: 0
PlasmaComponents.TabBar {
id: tabBar
tabPosition: Qt.LeftEdge
anchors.top: parent.top
PlasmaComponents.TabButton {
//text: qsTr("Directories")
iconSource: "folder-symbolic"
Layout.preferredWidth: 32
Layout.preferredHeight: 32
tab: dirsPage
}
PlasmaComponents.TabButton {
//text: qsTr("Devices")
iconSource: "network-server-symbolic"
tab: devicesPage
}
PlasmaComponents.TabButton {
//text: qsTr("Downloads")
iconSource: "folder-download-symbolic"
tab: downloadsPage
}
}
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")
}
}
}
}

View File

@ -0,0 +1,15 @@
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
PlasmaComponents.ListItem {
id: item
property bool expanded: false
enabled: true
onClicked: expanded = !expanded
onContainsMouseChanged: view.currentIndex = containsMouse ? index : -1
}

View File

@ -0,0 +1,20 @@
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
ListView {
anchors.fill: parent
boundsBehavior: Flickable.StopAtBounds
interactive: contentHeight > height
keyNavigationEnabled: true
keyNavigationWraps: true
currentIndex: -1
highlightMoveDuration: 0
highlightResizeDuration: 0
highlight: PlasmaComponents.Highlight { }
}

View File

@ -0,0 +1,51 @@
import QtQuick 2.0
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.kquickcontrolsaddons 2.0
import org.kde.plasma.extras 2.0 as PlasmaExtras
Item {
id: syncthingApplet
Plasmoid.switchWidth: units.gridUnit * 20
Plasmoid.switchHeight: units.gridUnit * 30
Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation
Plasmoid.compactRepresentation: CompactRepresentation { }
Plasmoid.fullRepresentation: FullRepresentation {
focus: true
}
Plasmoid.icon: "syncthingtray"
Plasmoid.toolTipMainText: plasmoid.nativeInterface.statusText
Plasmoid.toolTipSubText: plasmoid.nativeInterface.additionalStatusText
Plasmoid.hideOnWindowDeactivate: true
function action_showWebUI() {
plasmoid.nativeInterface.showWebUI()
}
function action_showSettings() {
plasmoid.nativeInterface.showConnectionSettingsDlg()
}
function action_rescanAllDirs() {
plasmoid.nativeInterface.connection.rescanAllDirs()
}
function action_showLog() {
plasmoid.nativeInterface.showLog()
}
function action_showAboutDialog() {
plasmoid.nativeInterface.connection.showAboutDialog()
}
Component.onCompleted: {
plasmoid.removeAction("configure");
plasmoid.setAction("showWebUI", qsTr("Web UI"), "internet-web-browser");
plasmoid.setAction("showSettings", qsTr("Settings"), "configure");
plasmoid.setAction("showLog", qsTr("Log"), "text-x-generic");
plasmoid.setAction("showAboutDialog", qsTr("About"), "help-about");
}
}

View File

@ -0,0 +1,21 @@
[Desktop Entry]
Encoding=UTF-8
Name=Syncthing Plasmoid
Icon=syncthingtray
Type=Service
Keywords=syncthing;sync;
X-KDE-ParentApp=
X-KDE-PluginInfo-Author=Martchus
X-KDE-PluginInfo-Email=martchus@gmx.net
X-KDE-PluginInfo-License=GPLv2
X-KDE-PluginInfo-Name=martchus.syncthingplasmoid
X-KDE-PluginInfo-Version=0.1
X-KDE-PluginInfo-Website=https://github.com/martchus/syncthingtray
X-KDE-ServiceTypes=Plasma/Applet
X-Plasma-NotificationArea=true
X-Plasma-API=declarativeappletscript
X-Plasma-MainScript=ui/main.qml
X-Plasma-RemoteLocation=
X-KDE-PluginInfo-EnabledByDefault=true
X-KDE-PluginInfo-Category=System Information
X-KDE-Library=syncthingplasmoid

36
plasmoid/testing.md Normal file
View File

@ -0,0 +1,36 @@
# Testing and debugging Plasma 5 plasmoid with Qt Creator
The following instructions allow to test the Plasmoid by installing it in a test directory
rather than the regular home to separate testing from production.
1. Build as usual, ensure `NO_PLASMOID` is turned off
2. Add build step to execute custom target `init_plasmoid_testing` which
will install the Plasmoid in a test directory which is "$BUILD_DIR/plasmoid/testdir"
by default
3. Add new config for run in Qt Creator and set `plasmoidviewer` (or `plasmawindowed`)
as executable
4. In execution environment, set
* `QT_PLUGIN_PATH` to directory containing plugin `\*.so`-file
* `QT_DEBUG_PLUGINS` to 1 for verbose plugin detection
* `HOME` to the test directory from step 2 so plasmoidviewer finds the Plasmoid
in the test directory
5. Set `--applet martchus.syncthingplasmoid` as CLI argument
6. Ignore warning that executable is no debug build, it is sufficiant when
the plugin is a debug build (see next section for QML debugging)
## Enable QML debugging
To enable QML debugging, it is required to rebuild `plasmoidviewer` with QML debugging
enabled.
1. Get plasma-sdk: `git clone https://anongit.kde.org/plasma-sdk.git`
2. Create a debug build of `plasmoidviewer` and ensure `QT_QML_DEBUG` is defined when
compiling `plasmoidviewer`, eg. by adding the following lines to its project file:
```
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(plasmoidviewer PRIVATE -DQT_QML_DEBUG)
endif()
```
3. Prepend the build directory containing the `plasmoidviewer` binary to the path variable
in the build environment of Syncthing Tray.
4. Enable QML debugging in the *Run* section.