Port Plasmoid to Plasma 6

* Split QML part into distinct versions for Plasma 5 and 6 as there are too
  many differences and `#ifdef` is not possible
* Change API usage according to
  https://develop.kde.org/docs/plasma/widget/porting_kf6
* Port "contextualActions" to API documented on
  https://invent.kde.org/frameworks/plasma-framework/-/blob/master/src/plasma/applet.h
* Document testing
* Add FIXMEs/notes for remaining problems; there are likely many more,
  though
This commit is contained in:
Martchus 2023-05-21 20:14:24 +02:00
parent 7e651be694
commit 912dca1564
46 changed files with 1879 additions and 55 deletions

View File

@ -1,12 +1,28 @@
# Testing and debugging Dolphin/KIO plugin with Qt Creator
1. Build as usual, ensure `NO_FILE_ITEM_ACTION_PLUGIN` is turned off
2. Copy `*.desktop` file to `~/.local/share/kservices5` and enable it in Dolphin
if packaged version is not already installed
3. Add new config for run in Qt Creator and set `dolphin` as executable
1. Build as usual, ensure `NO_FILE_ITEM_ACTION_PLUGIN` is turned off.
2. Copy `*.desktop` file to `~/.local/share/kservices5` and enable it in Dolphin if a packaged version
with the same configuration name is not already installed. Replace `5` with the current major version
of KDE.
3. Add new config for run in Qt Creator and set `dolphin` 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
* `QT_XCB_NO_GRAB_SERVER` to 1 to prevent grabbing so the debugger is usable
5. Ignore warning that executable is no debug build, it is sufficiant when
plugin is debug build
* `QT_PLUGIN_PATH` to directory containing plugin `\*.so`-file.
* `QT_DEBUG_PLUGINS` to 1 for verbose plugin detection.
* `QT_XCB_NO_GRAB_SERVER` to 1 to prevent grabbing so the debugger is usable.
5. Ignore warning that executable is no debug build, it is sufficiant when plugin is debug build.
## Testing against a development build of Dolphin
NOTE: This does not actually work with a KF6 build. Maybe the `*.desktop` file needs to be changed.
1. Build the whole dependency chain up to `dolphin` installing it under some custom prefix.
2. Then follow the usual steps but make sure you build Syncthing Tray against the custom KDE builds.
This is achieved the easiest by using the `debug-kde` CMake preset. This preset uses the environment
variable `KDE_INSTALL_DIR` which must point to the custom prefix used in step 1.
3. Copy the `*.desktop` file into the custom prefix used in step 1 under `share/kservices6`. Replace `6`
with the current major version of KDE. The directory likely needs to be created first.
4. Source the `prefix.sh` script that should be present in the build directory of any KDE library
you built in step 1, e.g. `source kde/plasma-sdk/prefix.sh`.
5. When setting the environment one needs to be more careful to not override variables set in step 4.
It is the easiest to just start e.g. `plasmawindowed` from the shell:
```
QT_PLUGIN_PATH=$BUILD_DIR/syncthingtray/debug-kde/syncthingtray/fileitemactionplugin:$QT_PLUGIN_PATH dolphin
```

3
plasmoid/.gitignore vendored
View File

@ -1,2 +1,3 @@
# the generated desktop file is required by inittesting.sh
# the generated desktop/JSON file is required by inittesting.sh
metadata.desktop
metadata.json

View File

@ -11,38 +11,37 @@ set(META_QT5_VERSION 5.8)
# use testfiles directory from syncthingconnector
set(META_SRCDIR_REFS "${CMAKE_CURRENT_SOURCE_DIR}\n${CMAKE_CURRENT_SOURCE_DIR}/../syncthingconnector")
# find Plasma
find_package(${KF_PACKAGE_PREFIX}Plasma REQUIRED)
# source files
set(PLASMOID_FILES_BASE "package${KF_MAJOR_VERSION}")
set(PLASMOID_FILES_UI "${PLASMOID_FILES_BASE}/contents/ui")
set(PLASMOID_FILES
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/RecentChangesPage.qml
package/contents/ui/TopLevelView.qml
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
package/contents/ui/StatisticsView.qml
package/contents/ui/main.qml)
${PLASMOID_FILES_UI}/CompactRepresentation.qml
${PLASMOID_FILES_UI}/FullRepresentation.qml
${PLASMOID_FILES_UI}/DirectoriesPage.qml
${PLASMOID_FILES_UI}/DevicesPage.qml
${PLASMOID_FILES_UI}/DownloadsPage.qml
${PLASMOID_FILES_UI}/RecentChangesPage.qml
${PLASMOID_FILES_UI}/TopLevelView.qml
${PLASMOID_FILES_UI}/TopLevelItem.qml
${PLASMOID_FILES_UI}/DetailView.qml
${PLASMOID_FILES_UI}/DetailItem.qml
${PLASMOID_FILES_UI}/TabButton.qml
${PLASMOID_FILES_UI}/ToolTipTrigger.qml
${PLASMOID_FILES_UI}/ToolTipView.qml
${PLASMOID_FILES_UI}/ToolBar.qml
${PLASMOID_FILES_UI}/ToolButton.qml
${PLASMOID_FILES_UI}/TinyButton.qml
${PLASMOID_FILES_UI}/IconLabel.qml
${PLASMOID_FILES_UI}/StatisticsView.qml
${PLASMOID_FILES_UI}/main.qml)
# find ECM (required by Plasma)
find_package(ECM REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_MODULE_PATH})
# find Plasma
set(KF_PACKAGE_PREFIX
"KF5"
CACHE STRING "specifies the prefix for KDE Frameworks packages")
find_package(${KF_PACKAGE_PREFIX}Plasma REQUIRED)
# find c++utilities
find_package(${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.20.0 REQUIRED)
list(APPEND CMAKE_MODULE_PATH ${CPP_UTILITIES_MODULE_DIRS})
@ -50,14 +49,22 @@ list(APPEND CMAKE_MODULE_PATH ${CPP_UTILITIES_MODULE_DIRS})
# prepare plasmoid package/configuration
set(PLASMOID_PACKAGE_DIR "${CMAKE_CURRENT_BINARY_DIR}/package")
file(MAKE_DIRECTORY "${PLASMOID_PACKAGE_DIR}")
set(PLASMOID_CONFIG_TARGET_FILE "${PLASMOID_PACKAGE_DIR}/metadata.desktop")
if (KF_MAJOR_VERSION LESS 6)
set(PLASMOID_CONFIG_TARGET_FILE "${PLASMOID_PACKAGE_DIR}/metadata.desktop")
else ()
set(PLASMOID_CONFIG_TARGET_FILE "${PLASMOID_PACKAGE_DIR}/metadata.json")
endif ()
# make plugin library
add_subdirectory(lib)
# make plasmoid configuration
include(TemplateFinder)
find_template_file("metadata.desktop" "${META_PROJECT_NAME}" PLASMOID_CONFIG_TEMPLATE_FILE)
if (KF_MAJOR_VERSION LESS 6)
find_template_file("metadata.desktop" "${META_PROJECT_NAME}" PLASMOID_CONFIG_TEMPLATE_FILE)
else ()
find_template_file("metadata.json" "${META_PROJECT_NAME}" PLASMOID_CONFIG_TEMPLATE_FILE)
endif ()
get_filename_component(PLASMOID_CONFIG_TARGET_FILE_ABSOLUTE_PATH "${PLASMOID_CONFIG_TARGET_FILE}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
configure_file("${PLASMOID_CONFIG_TEMPLATE_FILE}" "${PLASMOID_CONFIG_TARGET_FILE}")
@ -75,6 +82,6 @@ plasma_install_package("${PLASMOID_PACKAGE_DIR}" "${META_ID}")
set(PLASMOID_TESTDIR "${CMAKE_BINARY_DIR}" 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" "${PLASMOID_CONFIG_TARGET_FILE_ABSOLUTE_PATH}"
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/scripts/inittesting.sh" "${PLASMOID_CONFIG_TARGET_FILE_ABSOLUTE_PATH}" "${PLASMOID_FILES_BASE}"
WORKING_DIRECTORY "${PLASMOID_TESTDIR}"
)

View File

@ -0,0 +1,19 @@
{
"KPackageStructure": "Plasma/Applet",
"KPlugin": {
"Authors": [{"Name": "@META_APP_AUTHOR@"}],
"BugReportUrl": "@META_APP_BUGTRACKER_URL@",
"Category": "SystemServices",
"Description": "@META_APP_DESCRIPTION@",
"EnabledByDefault": false,
"Icon": "syncthingtray",
"Id": "@META_ID@",
"License": "@META_PROJECT_LICENSE@",
"Name": "Syncthing",
"Version": "@META_APP_VERSION@",
"Website": "@META_APP_URL@"
},
"Keywords": "syncthing;sync;",
"X-Plasma-API": "declarativeappletscript",
"X-Plasma-MainScript": "ui/main.qml"
}

View File

@ -34,6 +34,10 @@ use_syncthingwidgets()
# link also explicitly against the following Qt modules
list(APPEND ADDITIONAL_QT_MODULES Network Qml)
if (KF_MAJOR_VERSION GREATER_EQUAL 6)
set(Config_MODULE_TARGETS ${KF_PACKAGE_PREFIX}::ConfigCore)
list(APPEND ADDITIONAL_KF_MODULES Config)
endif ()
list(APPEND ADDITIONAL_KF_MODULES Plasma)
include(BasicConfig)
@ -55,9 +59,16 @@ set(META_PROJECT_LICENSE
"${META_PROJECT_LICENSE}"
PARENT_SCOPE)
# what ever this does, it is done
kcoreaddons_desktop_to_json("${META_TARGET_NAME}" "${PLASMOID_CONFIG_TARGET_FILE}" DESKTOP_TO_JSON_OUTPUT_DIR
"${PLASMOID_PACKAGE_DIR}")
# convert meta-data file to JSON
if (KF_MAJOR_VERSION LESS 6)
kcoreaddons_desktop_to_json("${META_TARGET_NAME}" "${PLASMOID_CONFIG_TARGET_FILE}" DESKTOP_TO_JSON_OUTPUT_DIR
"${PLASMOID_PACKAGE_DIR}")
endif ()
# set the library prefix so it doesn't start with "lib" and is rather prefixed with "martchus."
if (KF_MAJOR_VERSION GREATER_EQUAL 6)
set_target_properties(${META_TARGET_NAME} PROPERTIES PREFIX "martchus.")
endif ()
# install appstream file
add_appstream_file()

View File

@ -69,7 +69,11 @@ static inline QPalette paletteFromTheme(const Plasma::Theme &theme)
}
SyncthingApplet::SyncthingApplet(QObject *parent, const QVariantList &data)
#if PLASMA_VERSION >= QT_VERSION_CHECK(5, 240, 0)
: Applet(parent, KPluginMetaData(), data)
#else
: Applet(parent, data)
#endif
, m_faUrl(QStringLiteral("image://fa/"))
, m_iconManager(IconManager::instance(&m_palette))
, m_aboutDlg(nullptr)
@ -678,6 +682,12 @@ void SyncthingApplet::handleSystemdStatusChanged()
} // namespace Plasmoid
#if PLASMA_VERSION >= QT_VERSION_CHECK(5, 240, 0)
namespace Plasmoid {
K_PLUGIN_CLASS(SyncthingApplet)
}
#else
K_EXPORT_PLASMA_APPLET_WITH_JSON(syncthing, Plasmoid::SyncthingApplet, "metadata.json")
#endif
#include "syncthingapplet.moc"

View File

@ -95,7 +95,6 @@ PlasmaExtras.Representation {
id: searchButton
anchors.right: mainLayout.right
anchors.rightMargin: PlasmaCore.Units.smallSpacing * 2
anchors.verticalCenter: infoLayout.verticalCenter
icon.source: plasmoid.nativeInterface.faUrl + "search"
width: PlasmaCore.Units.iconSizes.smallMedium
height: width

View File

@ -0,0 +1,17 @@
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
MouseArea {
Layout.fillWidth: false
hoverEnabled: true
onClicked: plasmoid.expanded = !plasmoid.expanded
PlasmaCore.IconItem {
id: icon
anchors.fill: parent
source: plasmoid.statusIcon
active: parent.containsMouse
}
}

View File

@ -0,0 +1,50 @@
import QtQuick 2.7
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.kirigami 2.20 as Kirigami
Item {
id: detailItem
property string detailName: name ? name : ""
property string detailValue: detail ? detail : ""
width: detailRow.implicitWidth
height: detailRow.implicitHeight
RowLayout {
id: detailRow
width: parent.width
PlasmaCore.IconItem {
source: detailIcon
Layout.leftMargin: Kirigami.Units.iconSizes.small * 1.1
Layout.preferredWidth: Kirigami.Units.iconSizes.small
Layout.preferredHeight: Kirigami.Units.iconSizes.small
opacity: 0.8
}
PlasmaComponents3.Label {
Layout.preferredWidth: 100
text: detailName
font.weight: Font.DemiBold
}
PlasmaComponents3.Label {
Layout.leftMargin: theme.defaultFont.pointSize * 0.9
Layout.fillWidth: true
text: detailValue
elide: Text.ElideRight
horizontalAlignment: Qt.AlignRight
}
}
MouseArea {
acceptedButtons: Qt.RightButton
anchors.fill: parent
onClicked: {
var view = detailItem.ListView.view
var coordinates = mapToItem(view, mouseX, mouseY)
view.showContextMenu(detailItem, coordinates.x, coordinates.y)
}
}
}

View File

@ -0,0 +1,29 @@
import QtQuick 2.7
import org.kde.plasma.extras 2.0 as PlasmaExtras
ListView {
id: detailView
property DetailItem contextMenuItem: null
currentIndex: -1
interactive: false
height: contentHeight
PlasmaExtras.Menu {
id: contextMenu
PlasmaExtras.MenuItem {
text: qsTr('Copy value')
icon: "edit-copy"
onClicked: {
var item = detailView.contextMenuItem
if (item) {
plasmoid.copyToClipboard(item.detailValue)
}
}
}
}
function showContextMenu(item, x, y) {
contextMenuItem = item
contextMenu.open(x, y)
}
}

View File

@ -0,0 +1,125 @@
import QtQuick 2.3
import QtQuick.Layouts 1.1
import QtQml.Models 2.2
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.kirigami 2.20 as Kirigami
Item {
property alias view: deviceView
objectName: "DevicesPage"
PlasmaComponents3.ScrollView {
anchors.fill: parent
// HACK: workaround for https://bugreports.qt.io/browse/QTBUG-83890
PlasmaComponents3.ScrollBar.horizontal.policy: PlasmaComponents3.ScrollBar.AlwaysOff
contentItem: TopLevelView {
id: deviceView
model: plasmoid.sortFilterDevModel
delegate: TopLevelItem {
id: item
width: deviceView.effectiveWidth()
readonly property string devName: name
readonly property string devID: devId
property alias resumePauseButton: resumePauseButton
ColumnLayout {
width: parent.width
spacing: 0
RowLayout {
Layout.fillWidth: true
PlasmaCore.IconItem {
Layout.preferredWidth: Kirigami.Units.iconSizes.small
Layout.preferredHeight: Kirigami.Units.iconSizes.small
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
source: statusIcon
}
PlasmaComponents3.Label {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
elide: Text.ElideRight
text: name
}
RowLayout {
id: toolButtonsLayout
spacing: 0
PlasmaComponents3.Label {
height: implicitHeight
text: statusString
color: statusColor ? statusColor : PlasmaCore.ColorScope.textColor
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
}
Item {
width: 3
}
TinyButton {
id: resumePauseButton
icon.source: plasmoid.faUrl + (paused ? "play" : "pause")
icon.cache: false
tooltip: paused ? qsTr("Resume") : qsTr("Pause")
enabled: !isOwnDevice
onClicked: {
paused ? plasmoid.connection.resumeDevice(
[devId]) : plasmoid.connection.pauseDevice(
[devId])
}
}
}
}
DetailView {
id: detailsView
visible: item.expanded
Layout.fillWidth: true
Layout.topMargin: 3
model: DelegateModel {
model: plasmoid.devModel
rootIndex: deviceView.model.mapToSource(deviceView.model.index(index, 0))
delegate: DetailItem {
width: detailsView.width
}
}
}
}
}
PlasmaExtras.Menu {
id: contextMenu
function init(item) {
// use value for properties depending on paused state from buttons
resumePauseItem.text = item.resumePauseButton.tooltip
resumePauseItem.icon = item.resumePauseButton.icon
}
PlasmaExtras.MenuItem {
text: qsTr("Copy name")
icon: "edit-copy"
onClicked: deviceView.copyCurrentItemData("devName")
}
PlasmaExtras.MenuItem {
text: qsTr("Copy ID")
icon: "edit-copy"
onClicked: deviceView.copyCurrentItemData("devID")
}
PlasmaExtras.MenuItem {
separator: true
}
PlasmaExtras.MenuItem {
id: resumePauseItem
text: qsTr("Pause")
icon: "media-playback-pause"
onClicked: deviceView.clickCurrentItemButton(
"resumePauseButton")
}
}
}
}
}

View File

@ -0,0 +1,186 @@
import QtQuick 2.3
import QtQuick.Layouts 1.1
import QtQml.Models 2.2
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.kirigami 2.20 as Kirigami
ColumnLayout {
property alias view: directoryView
property alias filter: filter
objectName: "DirectoriesPage"
PlasmaComponents3.TextField {
Layout.topMargin: Kirigami.Units.smallSpacing * 2
Layout.leftMargin: Kirigami.Units.smallSpacing * 2
Layout.rightMargin: Kirigami.Units.smallSpacing * 2
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)
}
PlasmaComponents3.ScrollView {
Layout.fillWidth: true
Layout.fillHeight: true
// HACK: workaround for https://bugreports.qt.io/browse/QTBUG-83890
PlasmaComponents3.ScrollBar.horizontal.policy: PlasmaComponents3.ScrollBar.AlwaysOff
contentItem: TopLevelView {
id: directoryView
model: plasmoid.sortFilterDirModel
delegate: TopLevelItem {
id: item
width: directoryView.effectiveWidth()
readonly property string dirName: name
readonly property string dirPath: path
property alias errorsButton: errorsButton
property alias rescanButton: rescanButton
property alias resumePauseButton: resumePauseButton
property alias openButton: openButton
ColumnLayout {
width: parent.width
spacing: 0
RowLayout {
id: itemSummary
Layout.fillWidth: true
PlasmaCore.IconItem {
Layout.preferredWidth: units.iconSizes.small
Layout.preferredHeight: units.iconSizes.small
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
source: statusIcon
}
PlasmaComponents3.Label {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
elide: Text.ElideRight
text: name
}
RowLayout {
id: toolButtonsLayout
spacing: 0
PlasmaComponents3.Label {
height: implicitHeight
text: statusString
color: statusColor ? statusColor : PlasmaCore.ColorScope.textColor
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
}
Item {
width: units.smallSpacing
}
TinyButton {
id: errorsButton
icon.source: plasmoid.faUrl + "exclamation-triangle"
tooltip: qsTr("Show errors")
visible: pullErrorCount > 0
onClicked: {
plasmoid.showDirectoryErrors(
dirId)
plasmoid.expanded = false
}
}
TinyButton {
id: rescanButton
icon.source: plasmoid.faUrl + "refresh"
tooltip: qsTr("Rescan")
enabled: !paused
onClicked: plasmoid.connection.rescan(
dirId)
}
TinyButton {
id: resumePauseButton
icon.source: plasmoid.faUrl + (paused ? "play" : "pause")
tooltip: paused ? qsTr("Resume") : qsTr("Pause")
onClicked: {
paused ? plasmoid.connection.resumeDirectories(
[dirId]) : plasmoid.connection.pauseDirectories(
[dirId])
}
}
TinyButton {
id: openButton
icon.source: plasmoid.faUrl + "folder"
tooltip: qsTr("Open in file browser")
onClicked: {
Qt.openUrlExternally(plasmoid.substituteTilde(path))
plasmoid.expanded = false
}
}
}
}
DetailView {
id: detailsView
visible: item.expanded
Layout.fillWidth: true
Layout.topMargin: 3
model: DelegateModel {
model: plasmoid.dirModel
rootIndex: directoryView.model.mapToSource(directoryView.model.index(index, 0))
delegate: DetailItem {
width: detailsView.width
}
}
}
}
}
PlasmaExtras.Menu {
id: contextMenu
function init(item) {
// use value for properties depending on paused state from buttons
rescanItem.enabled = item.rescanButton.enabled
resumePauseItem.text = item.resumePauseButton.tooltip
resumePauseItem.icon = item.resumePauseButton.icon
}
PlasmaExtras.MenuItem {
text: qsTr("Copy label/ID")
icon: "edit-copy"
onClicked: directoryView.copyCurrentItemData("dirName")
}
PlasmaExtras.MenuItem {
text: qsTr("Copy path")
icon: "edit-copy"
onClicked: directoryView.copyCurrentItemData("dirPath")
}
PlasmaExtras.MenuItem {
separator: true
}
PlasmaExtras.MenuItem {
id: rescanItem
text: qsTr("Rescan")
icon: "view-refresh"
onClicked: directoryView.clickCurrentItemButton(
"rescanButton")
}
PlasmaExtras.MenuItem {
id: resumePauseItem
text: qsTr("Pause")
icon: "media-playback-pause"
onClicked: directoryView.clickCurrentItemButton(
"resumePauseButton")
}
PlasmaExtras.MenuItem {
id: openItem
text: qsTr("Open in file browser")
icon: "folder"
onClicked: directoryView.clickCurrentItemButton(
"openButton")
}
}
}
}
}

View File

@ -0,0 +1,155 @@
import QtQuick 2.3
import QtQuick.Layouts 1.1
import QtQml.Models 2.2
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.kirigami 2.20 as Kirigami
Item {
property alias view: downloadView
objectName: "DownloadsPage"
PlasmaComponents3.ScrollView {
anchors.fill: parent
// HACK: workaround for https://bugreports.qt.io/browse/QTBUG-83890
PlasmaComponents3.ScrollBar.horizontal.policy: PlasmaComponents3.ScrollBar.AlwaysOff
contentItem: TopLevelView {
id: downloadView
model: plasmoid.downloadModel
delegate: TopLevelItem {
id: item
width: downloadView.effectiveWidth()
readonly property string downloadName: name
property alias openButton: openButton
ColumnLayout {
width: parent.width
spacing: 0
RowLayout {
id: itemSummary
Layout.fillWidth: true
RowLayout {
spacing: units.smallSpacing
PlasmaComponents3.Label {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
elide: Text.ElideRight
text: name ? name : "?"
}
}
PlasmaComponents3.ProgressBar {
Layout.fillWidth: true
Layout.fillHeight: true
from: 0.0
to: 100.0
value: percentage ? percentage : 0.0
}
RowLayout {
id: toolButtonsLayout
spacing: 0
PlasmaComponents3.Label {
height: implicitHeight
text: progressLabel ? progressLabel : ""
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
}
Item {
width: 3
}
TinyButton {
id: openButton
icon.source: plasmoid.faUrl + "folder"
tooltip: qsTr("Open in file browser")
enabled: path !== undefined
onClicked: {
Qt.openUrlExternally(path)
plasmoid.expanded = false
}
}
}
}
DetailView {
id: detailsView
visible: item.expanded
Layout.fillWidth: true
model: DelegateModel {
model: plasmoid.downloadModel
rootIndex: detailsView.model.modelIndex(index)
delegate: RowLayout {
width: detailsView.width
PlasmaCore.IconItem {
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
source: fileIcon
}
ColumnLayout {
spacing: 0
Layout.fillWidth: true
RowLayout {
spacing: Kirigami.Units.smallSpacing
Layout.fillWidth: true
PlasmaComponents3.Label {
Layout.fillWidth: true
text: name
font.pointSize: theme.defaultFont.pointSize * 0.8
elide: Text.ElideRight
}
PlasmaComponents3.Label {
text: progressLabel
font.pointSize: theme.defaultFont.pointSize * 0.8
elide: Text.ElideRight
}
}
PlasmaComponents3.ProgressBar {
Layout.fillWidth: true
Layout.preferredHeight: 8
Layout.topMargin: 0
from: 0.0
to: 100.0
value: percentage
}
}
TinyButton {
icon.source: plasmoid.faUrl + "folder"
tooltip: qsTr("Open in file browser")
onClicked: {
Qt.openUrlExternally(path + "/..")
plasmoid.expanded = false
}
}
}
}
}
}
}
PlasmaExtras.Menu {
id: contextMenu
PlasmaExtras.MenuItem {
text: qsTr("Copy label/ID")
icon: "edit-copy"
onClicked: downloadView.copyCurrentItemData("downloadName")
}
PlasmaExtras.MenuItem {
separator: true
}
PlasmaExtras.MenuItem {
id: openItem
text: qsTr("Open in file browser")
icon: "folder"
onClicked: downloadView.clickCurrentItemButton("openButton")
}
}
}
}
}

View File

@ -0,0 +1,303 @@
import QtQml 2.2
import QtQuick 2.8
import QtQuick.Layouts 1.3
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 3.0 as PlasmaComponents3
import org.kde.kirigami 2.20 as Kirigami
PlasmaExtras.Representation {
// disable margins as they don't look good together with the scroll view
// note: Would be collapsed automatically if the scroll view was the immediate content item.
collapseMarginsHint: true
// header ("toolbar" with buttons and combo box) and footer ("tabbar")
header: PlasmaExtras.PlasmoidHeading {
ToolBar {
id: toolbar
width: parent.width
}
}
footer: PlasmaExtras.PlasmoidHeading {
spacing: 0
topPadding: 0
height: Kirigami.Units.iconSizes.medium
PlasmaComponents3.TabBar {
id: tabBar
readonly property double buttonWidth: parent.width / count
position: PlasmaComponents3.TabBar.Footer
Layout.fillWidth: true
Layout.fillHeight: true
TabButton {
id: dirsTabButton
text: qsTr("Directories")
icon.source: plasmoid.faUrl + "folder"
width: tabBar.buttonWidth
}
TabButton {
id: devsTabButton
text: qsTr("Devices")
icon.source: plasmoid.faUrl + "sitemap"
width: tabBar.buttonWidth
}
TabButton {
id: downloadsTabButton
text: qsTr("Downloads")
icon.source: plasmoid.faUrl + "download"
width: tabBar.buttonWidth
}
TabButton {
id: recentChangesTabButton
text: qsTr("History")
icon.source: plasmoid.faUrl + "history"
width: tabBar.buttonWidth
}
}
}
// 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) {
findCurrentPage().view.clickCurrentItemButton(buttonName)
}
Shortcut {
sequence: "Ctrl+R"
onActivated: clickCurrentItemButton("rescanButton")
}
Shortcut {
sequence: "Ctrl+P"
onActivated: clickCurrentItemButton("resumePauseButton")
}
Shortcut {
sequence: "Ctrl+O"
onActivated: clickCurrentItemButton("openButton")
}
// main contents
FocusScope {
anchors.fill: parent
anchors.topMargin: Kirigami.Units.smallSpacing * 2
TinyButton {
id: searchButton
anchors.right: mainLayout.right
anchors.rightMargin: Kirigami.Units.smallSpacing * 2
icon.source: plasmoid.faUrl + "search"
width: Kirigami.Units.iconSizes.smallMedium
height: width
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 = ""
}
}
}
ColumnLayout {
id: mainLayout
anchors.fill: parent
spacing: 0
// ensure keyboard events can be received after initialization
Component.onCompleted: forceActiveFocus()
// define custom key handling for switching tabs, selecting items and filtering
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()
}
Keys.onPressed: function(event) {
// 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.currentConnectionConfigIndex
break
}
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.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 {
id: infoLayout
Layout.leftMargin: Kirigami.Units.smallSpacing * 2
Layout.rightMargin: Kirigami.Units.smallSpacing * 2
Layout.fillWidth: true
Layout.fillHeight: false
Layout.maximumWidth: parent.width
columns: 4
rowSpacing: 1
columnSpacing: 4
Image {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
height: 16
fillMode: Image.PreserveAspectFit
source: plasmoid.faUrl + "globe"
cache: false
}
StatisticsView {
Layout.leftMargin: 4
statistics: plasmoid.globalStatistics
context: qsTr("Global")
}
IconLabel {
Layout.leftMargin: 5
iconSource: plasmoid.faUrl + "cloud-download"
iconOpacity: plasmoid.hasIncomingTraffic ? 1.0 : 0.5
text: plasmoid.incomingTraffic
tooltip: qsTr("Global incoming traffic")
}
Item {
Layout.fillWidth: true
Layout.rowSpan: 2
}
Image {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
height: 16
fillMode: Image.PreserveAspectFit
source: plasmoid.faUrl + "home"
cache: false
}
StatisticsView {
Layout.leftMargin: 4
statistics: plasmoid.localStatistics
context: qsTr("Local")
}
IconLabel {
Layout.leftMargin: 5
iconSource: plasmoid.faUrl + "cloud-upload"
iconOpacity: plasmoid.hasOutgoingTraffic ? 1.0 : 0.5
text: plasmoid.outgoingTraffic
tooltip: qsTr("Global outgoing traffic")
}
}
// tab content
StackLayout {
id: tabLayout
currentIndex: tabBar.currentIndex
Layout.fillWidth: true
Layout.fillHeight: true
DirectoriesPage {
id: directoriesPage
}
DevicesPage {
id: devicesPage
}
DownloadsPage {
id: downloadsPage
}
RecentChangesPage {
id: recentChangesPage
}
}
}
}
}

View File

@ -0,0 +1,35 @@
import QtQuick 2.0
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents3
Item {
property alias iconSource: iconItem.source
property alias iconOpacity: iconItem.opacity
property alias text: label.text
property alias tooltip: tooltipTrigger.tooltip
implicitWidth: layout.implicitWidth
implicitHeight: layout.implicitHeight
RowLayout {
id: layout
Image {
id: iconItem
Layout.preferredWidth: 16
Layout.preferredHeight: 16
height: 16
cache: false
fillMode: Image.PreserveAspectFit
}
PlasmaComponents3.Label {
id: label
}
}
ToolTipTrigger {
id: tooltipTrigger
anchors.fill: layout
}
}

View File

@ -0,0 +1,127 @@
import QtQuick 2.3
import QtQuick.Layouts 1.1
import QtQml.Models 2.2
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.kirigami 2.20 as Kirigami
Item {
property alias view: recentChangesView
objectName: "RecentChangesPage"
PlasmaComponents3.ScrollView {
anchors.fill: parent
// HACK: workaround for https://bugreports.qt.io/browse/QTBUG-83890
PlasmaComponents3.ScrollBar.horizontal.policy: PlasmaComponents3.ScrollBar.AlwaysOff
contentItem: TopLevelView {
id: recentChangesView
model: plasmoid.recentChangesModel
delegate: TopLevelItem {
width: recentChangesView.effectiveWidth()
ColumnLayout {
width: parent.width
spacing: 0
RowLayout {
Layout.fillWidth: true
PlasmaCore.IconItem {
Layout.preferredWidth: Kirigami.Units.iconSizes.small
Layout.preferredHeight: Kirigami.Units.iconSizes.small
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
source: actionIcon
}
PlasmaComponents3.Label {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillWidth: true
elide: Text.ElideRight
text: extendedAction
}
Item {
width: Kirigami.Units.smallSpacing
}
Image {
Layout.preferredWidth: Kirigami.Units.iconSizes.small
Layout.preferredHeight: Kirigami.Units.iconSizes.small
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
height: parent.height
fillMode: Image.PreserveAspectFit
source: plasmoid.faUrl + "calendar"
}
PlasmaComponents3.Label {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
elide: Text.ElideRight
text: eventTime
}
Item {
width: Kirigami.Units.smallSpacing
}
Image {
Layout.preferredWidth: Kirigami.Units.iconSizes.small
Layout.preferredHeight: Kirigami.Units.iconSizes.small
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
height: parent.height
fillMode: Image.PreserveAspectFit
source: plasmoid.faUrl + "qrcode"
}
PlasmaComponents3.Label {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
elide: Text.ElideRight
text: modifiedBy
}
}
RowLayout {
Layout.fillWidth: true
Image {
Layout.preferredWidth: Kirigami.Units.iconSizes.small
Layout.preferredHeight: Kirigami.Units.iconSizes.small
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
height: parent.height
fillMode: Image.PreserveAspectFit
source: plasmoid.faUrl + (itemType === "file" ? "file-o" : "folder-o")
}
PlasmaComponents3.Label {
text: directoryId + ": "
font.weight: Font.DemiBold
}
PlasmaComponents3.Label {
Layout.fillWidth: true
text: path
elide: Text.ElideRight
}
}
}
function copyPath() {
plasmoid.copyToClipboard(path)
}
function copyDeviceId() {
plasmoid.copyToClipboard(modifiedBy)
}
function copyFolderId() {
plasmoid.copyToClipboard(folderId)
}
}
PlasmaExtras.Menu {
id: contextMenu
PlasmaExtras.MenuItem {
text: qsTr("Copy path")
icon: "edit-copy"
onClicked: recentChangesView.currentItem.copyPath()
}
PlasmaExtras.MenuItem {
text: qsTr("Copy device ID")
icon: "network-server-symbolic"
onClicked: recentChangesView.currentItem.copyDeviceId()
}
PlasmaExtras.MenuItem {
text: qsTr("Copy directory ID")
icon: "folder"
onClicked: recentChangesView.currentItem.copyFolderId()
}
}
}
}
}

View File

@ -0,0 +1,25 @@
import QtQuick 2.7
import QtQuick.Layouts 1.1
RowLayout {
id: rowLayout
property var statistics
property string context: "?"
IconLabel {
iconSource: plasmoid.faUrl + "file-o"
text: statistics.files !== undefined ? statistics.files : "?"
tooltip: context + qsTr(" files")
}
IconLabel {
iconSource: plasmoid.faUrl + "folder-o"
text: statistics.dirs !== undefined ? statistics.dirs : "?"
tooltip: context + qsTr(" directories")
}
IconLabel {
iconSource: plasmoid.faUrl + "hdd-o"
text: statistics.bytes !== undefined ? plasmoid.formatFileSize(
statistics.bytes) : "?"
tooltip: context + qsTr(" size")
}
}

View File

@ -0,0 +1,48 @@
import QtQuick 2.8
import QtQuick.Templates 2.15 as T
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.kirigami 2.5 as Kirigami
PlasmaComponents3.TabButton {
id: root
spacing: 0
property bool showTabText: plasmoid.showTabTexts
property string tooltip: ""
PlasmaComponents3.ToolTip {
id: tooltip
text: root.tooltip !== "" ? root.tooltip : (root.showTabText ? "" : root.text)
visible: text !== "" && parent instanceof T.AbstractButton && (Kirigami.Settings.tabletMode ? parent.pressed : parent.hovered)
}
contentItem: RowLayout {
spacing: label.visible ? Kirigami.Units.smallSpacing : 0
PlasmaCore.ColorScope.inherit: true
width: parent.width
Item {
Layout.fillWidth: true
}
Image {
id: image
Layout.preferredHeight: height
source: root.icon.source
cache: false
height: Kirigami.Units.iconSizes.small
fillMode: Image.PreserveAspectFit
}
PlasmaComponents3.Label {
id: label
visible: text.length > 0
text: root.showTabText ? root.text : ""
color: PlasmaCore.ColorScope.textColor
elide: Text.ElideRight
Layout.fillHeight: true
}
Item {
Layout.fillWidth: true
}
}
}

View File

@ -0,0 +1,24 @@
import QtQuick 2.8
import QtQuick.Layouts 1.1
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.kirigami 2.20 as Kirigami
PlasmaComponents3.ToolButton {
id: root
property alias tooltip: tooltip.text
Layout.fillHeight: true
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
PlasmaComponents3.ToolTip {
id: tooltip
}
contentItem: Image {
source: root.icon.source
cache: false
height: parent.height
fillMode: Image.PreserveAspectFit
}
}

View File

@ -0,0 +1,280 @@
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.kirigami 2.20 as Kirigami
import org.kde.kquickcontrolsaddons 2.0
import martchus.syncthingplasmoid 0.6 as SyncthingPlasmoid
RowLayout {
id: toolBar
Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing
Layout.minimumHeight: Kirigami.Units.iconSizes.medium
Layout.maximumHeight: Kirigami.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: plasmoid.faUrl + "refresh"
enabled: true
}
},
State {
name: "connecting"
PropertyChanges {
target: connectButton
text: qsTr("Connecting …")
icon.source: plasmoid.faUrl + "refresh"
enabled: false
}
},
State {
name: "paused"
PropertyChanges {
target: connectButton
text: qsTr("Resume")
icon.source: plasmoid.faUrl + "play"
enabled: true
}
},
State {
name: "idle"
PropertyChanges {
target: connectButton
text: qsTr("Pause")
icon.source: plasmoid.faUrl + "pause"
enabled: true
}
}
]
state: {
switch (plasmoid.connection.status) {
case SyncthingPlasmoid.Data.Disconnected:
return plasmoid.connection.connecting ? "connecting" : "disconnected"
case SyncthingPlasmoid.Data.Reconnecting:
return "connecting";
case SyncthingPlasmoid.Data.Paused:
return "paused"
default:
return "idle"
}
}
onClicked: {
switch (plasmoid.connection.status) {
case SyncthingPlasmoid.Data.Disconnected:
plasmoid.connection.connect()
break
case SyncthingPlasmoid.Data.Reconnecting:
break
case SyncthingPlasmoid.Data.Paused:
plasmoid.connection.resumeAllDevs()
break
default:
plasmoid.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: plasmoid.faUrl + "stop"
}
PropertyChanges {
target: startStopToolTip
text: (plasmoid.service.userScope ? "systemctl --user stop " : "systemctl stop ")
+ plasmoid.service.unitName
}
},
State {
name: "stopped"
PropertyChanges {
target: startStopButton
visible: true
text: qsTr("Start")
icon.source: plasmoid.faUrl + "play"
}
PropertyChanges {
target: startStopToolTip
text: (plasmoid.service.userScope ? "systemctl --user start " : "systemctl start ")
+ plasmoid.service.unitName
}
},
State {
name: "irrelevant"
PropertyChanges {
target: startStopButton
visible: false
}
}
]
state: {
// the systemd unit status is only relevant when connected to the local instance
if (!plasmoid.local || !plasmoid.startStopEnabled) {
return "irrelevant"
}
// show start/stop button only when the configured unit is available
var service = plasmoid.service
if (!service || !service.systemdAvailable) {
return "irrelevant"
}
return service.running ? "running" : "stopped"
}
onClicked: plasmoid.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.notificationsAvailable
onClicked: {
plasmoid.showNotificationsDialog()
plasmoid.expanded = false
}
PlasmaComponents3.ToolTip {
text: qsTr("Show new notifications")
}
Shortcut {
sequence: "Ctrl+N"
onActivated: {
if (showNewNotifications.visible) {
showNewNotifications.clicked()
}
}
}
}
ToolButton {
icon.source: plasmoid.faUrl + "info"
visible: showExtraButtons
onClicked: {
plasmoid.showAboutDialog()
plasmoid.expanded = false
}
PlasmaComponents3.ToolTip {
text: qsTr("About Syncthing Tray")
}
}
ToolButton {
id: showOwnIdButton
icon.source: plasmoid.faUrl + "qrcode"
visible: showExtraButtons
onClicked: {
plasmoid.showOwnDeviceId()
plasmoid.expanded = false
}
PlasmaComponents3.ToolTip {
text: qsTr("Show own device ID")
}
Shortcut {
sequence: "Ctrl+I"
onActivated: showOwnIdButton.clicked()
}
}
ToolButton {
id: showLogButton
icon.source: plasmoid.faUrl + "file-text"
visible: showExtraButtons
onClicked: {
plasmoid.showLog()
plasmoid.expanded = false
}
PlasmaComponents3.ToolTip {
text: qsTr("Show Syncthing log")
}
Shortcut {
sequence: "Ctrl+L"
onActivated: showLogButton.clicked()
}
}
ToolButton {
id: rescanAllDirsButton
icon.source: plasmoid.faUrl + "refresh"
onClicked: plasmoid.connection.rescanAllDirs()
PlasmaComponents3.ToolTip {
text: qsTr("Rescan all directories")
}
Shortcut {
sequence: "Ctrl+Shift+R"
onActivated: rescanAllDirsButton.clicked()
}
}
ToolButton {
id: settingsButton
icon.source: plasmoid.faUrl + "cog"
visible: showExtraButtons
onClicked: {
plasmoid.showSettingsDlg()
plasmoid.expanded = false
}
PlasmaComponents3.ToolTip {
text: qsTr("Settings")
}
Shortcut {
sequence: "Ctrl+S"
onActivated: settingsButton.clicked()
}
}
ToolButton {
id: webUIButton
icon.source: plasmoid.faUrl + "syncthing"
onClicked: {
plasmoid.showWebUI()
plasmoid.expanded = false
}
PlasmaComponents3.ToolTip {
text: qsTr("Open Syncthing")
}
Shortcut {
sequence: "Ctrl+W"
onActivated: webUIButton.clicked()
}
}
PlasmaComponents3.ComboBox {
id: connectionConfigsMenu
model: plasmoid.connectionConfigNames
visible: plasmoid.connectionConfigNames.length > 1
currentIndex: plasmoid.currentConnectionConfigIndex
onCurrentIndexChanged: plasmoid.currentConnectionConfigIndex = currentIndex
Layout.fillWidth: true
Layout.maximumWidth: implicitWidth
Shortcut {
sequence: "Ctrl+Shift+C"
onActivated: connectionConfigsMenu.popup()
}
}
}

View File

@ -0,0 +1,32 @@
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
import org.kde.kirigami 2.20 as Kirigami
PlasmaComponents3.ToolButton {
id: root
Layout.fillHeight: true
contentItem: Grid {
columns: 2
columnSpacing: label.visible ? Kirigami.Units.smallSpacing : 0
verticalItemAlignment: Grid.AlignVCenter
PlasmaCore.ColorScope.inherit: true
Image {
source: root.icon.source
cache: false
height: parent.height
fillMode: Image.PreserveAspectFit
}
PlasmaComponents3.Label {
id: label
visible: text.length > 0
text: root.text
color: PlasmaCore.ColorScope.textColor
elide: Text.ElideRight
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}

View File

@ -0,0 +1,17 @@
import QtQuick 2.0
import org.kde.plasma.components 3.0 as PlasmaComponents3
MouseArea {
property alias interval: timer.interval
property alias tooltip: tooltip.text
hoverEnabled: true
Timer {
id: timer
interval: 1000
running: parent.containsMouse && parent.tooltip.length !== 0
onTriggered: tooltip.open()
}
PlasmaComponents3.ToolTip {
id: tooltip
}
}

View File

@ -0,0 +1,37 @@
import QtQuick 2.0
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.kirigami 2.20 as Kirigami
RowLayout {
spacing: Kirigami.Units.smallSpacing
PlasmaCore.IconItem {
id: tooltipIcon
source: plasmoid.statusIcon
Layout.alignment: Qt.AlignCenter
visible: true
implicitWidth: Kirigami.Units.iconSizes.large
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.leftMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.rightMargin: Kirigami.Units.smallSpacing
Layout.preferredWidth: implicitWidth
Layout.preferredHeight: implicitWidth
}
ColumnLayout {
Kirigami.Heading {
id: tooltipMaintext
level: 3
elide: Text.ElideRight
text: plasmoid.statusText
}
PlasmaComponents3.Label {
id: tooltipSubtext
text: plasmoid.additionalStatusText
opacity: 0.6
}
}
}

View File

@ -0,0 +1,116 @@
// Based on PlasmaComponents.ListItem from Plasma 5.44.0
// (Can't use PlasmaComponents.ListItem itself because creating a MouseArea filling
// the entire entire item for detecting right-mouse-click is not possible due to binding
// loop of width and height properties.)
import QtQuick 2.7
import QtQuick.Layouts 1.1
import org.kde.ksvg 1.0 as KSvg
import org.kde.kirigami 2.20 as Kirigami
Item {
id: listItem
property bool expanded: false
default property alias content: paddingItem.data
/**
* If true makes the list item look as checked or pressed. It has to be set
* from the code, it won't change by itself.
*/
//plasma extension
//always look pressed?
property bool checked: false
/**
* If true the item will be a delegate for a section, so will look like a
* "title" for the otems under it.
*/
//is this to be used as section delegate?
property bool sectionDelegate: false
/**
* type: bool
* True if the separator between items is visible
* default: true
*/
property bool separatorVisible: true
width: parent ? parent.width : childrenRect.width
height: paddingItem.childrenRect.height + background.margins.top + background.margins.bottom
implicitHeight: paddingItem.childrenRect.height + background.margins.top
+ background.margins.bottom
function activate(containsMouse) {
view.activate(containsMouse ? index : -1)
}
KSvg.FrameSvgItem {
id: background
imagePath: "widgets/listitem"
prefix: (listItem.sectionDelegate ? "section" : (itemMouse.pressed
|| listItem.checked) ? "pressed" : "normal")
anchors.fill: parent
visible: listItem.ListView.view ? listItem.ListView.view.highlight === null : true
Behavior on opacity {
NumberAnimation {
duration: Kirigami.Units.longDuration
}
}
}
KSvg.SvgItem {
svg: KSvg.Svg {
imagePath: "widgets/listitem"
}
elementId: "separator"
anchors {
left: parent.left
right: parent.right
top: parent.top
}
height: naturalSize.height
visible: separatorVisible && (listItem.sectionDelegate
|| (typeof (index) != "undefined"
&& index > 0 && !listItem.checked
&& !itemMouse.pressed))
}
MouseArea {
id: itemMouse
property bool changeBackgroundOnPress: !listItem.checked
&& !listItem.sectionDelegate
anchors.fill: background
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: function(mouse) {
switch (mouse.button) {
case Qt.LeftButton:
expanded = !expanded
break
case Qt.RightButton:
var view = listItem.ListView.view
var coordinates = mapToItem(view, mouseX, mouseY)
view.showContextMenu(listItem, coordinates.x, coordinates.y)
break
}
}
onContainsMouseChanged: {
listItem.activate(containsMouse)
}
Item {
id: paddingItem
anchors {
fill: parent
leftMargin: background.margins.left
topMargin: background.margins.top
rightMargin: background.margins.right
bottomMargin: background.margins.bottom
}
}
}
Accessible.role: Accessible.ListItem
}

View File

@ -0,0 +1,64 @@
import QtQuick 2.7
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.kirigami 2.20 as Kirigami
ListView {
boundsBehavior: Flickable.StopAtBounds
interactive: contentHeight > height
keyNavigationEnabled: true
keyNavigationWraps: true
currentIndex: -1
highlightMoveDuration: 0
highlightResizeDuration: 0
highlight: PlasmaExtras.Highlight {
}
topMargin: Kirigami.Units.smallSpacing * 2
bottomMargin: Kirigami.Units.smallSpacing * 2
leftMargin: Kirigami.Units.smallSpacing * 2
rightMargin: Kirigami.Units.smallSpacing * 2
function effectiveWidth() {
return width - leftMargin - rightMargin
}
function activate(index) {
if (typeof contextMenu !== "undefined"
&& contextMenu.status !== PlasmaExtras.DialogStatus.Closed) {
return
}
currentIndex = index
}
function clickCurrentItemButton(buttonName) {
if (!currentItem) {
return
}
var button = currentItem[buttonName]
if (button && button.enabled) {
button.clicked()
}
}
function copyCurrentItemData(fieldName) {
if (!currentItem) {
return
}
var data = currentItem[fieldName]
if (data) {
plasmoid.copyToClipboard(data)
}
}
function showContextMenu(item, x, y) {
if (typeof contextMenu === "undefined") {
return
}
if (typeof contextMenu.init !== "undefined") {
contextMenu.init(item)
}
contextMenu.open(x, y)
}
}

View File

@ -0,0 +1,77 @@
import QtQuick 2.2
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.plasmoid 2.0
import org.kde.kquickcontrolsaddons 2.0
import org.kde.kirigami 2.20 as Kirigami
PlasmoidItem {
id: syncthingApplet
// FIXME: adding title causes plasmawindowed to segfault
//Plasmoid.title: "Syncthing"
Plasmoid.icon: "syncthing"
// FIXME: not sure whether assigning switchWidth/switchHeight like this works
switchWidth: fullRepresentationItem ? fullRepresentationItem.Layout.minimumWidth : -1
switchHeight: fullRepresentationItem ? fullRepresentationItem.Layout.minimumHeight : -1
preferredRepresentation: fullRepresentation
compactRepresentation: CompactRepresentation {}
fullRepresentation: FullRepresentation {
Layout.minimumWidth: Kirigami.Units.gridUnit * Plasmoid.size.width
Layout.minimumHeight: Kirigami.Units.gridUnit * Plasmoid.size.height
}
toolTipMainText: Plasmoid.statusText
toolTipSubText: Plasmoid.additionalStatusText
toolTipItem: ToolTipView {}
Plasmoid.contextualActions: [
PlasmaCore.Action {
text: qsTr("Open Syncthing")
icon.name: "syncthing"
onTriggered: Plasmoid.showWebUI()
},
PlasmaCore.Action {
text: qsTr("Rescan all directories")
icon.name: "folder-sync"
onTriggered: Plasmoid.connection.rescanAllDirs()
},
PlasmaCore.Action {
text: qsTr("Show own device ID")
icon.name: "view-barcode-qr"
onTriggered: Plasmoid.showOwnDeviceId()
},
PlasmaCore.Action {
text: qsTr("Restart Syncthing")
icon.name: "system-reboot"
onTriggered: Plasmoid.connection.restart()
},
PlasmaCore.Action {
text: qsTr("Log")
icon.name: "text-x-generic"
onTriggered: Plasmoid.showLog()
},
PlasmaCore.Action {
text: qsTr("Internal errors")
icon.name: "data-error"
onTriggered: Plasmoid.showInternalErrorsDialog()
visible: Plasmoid.hasInternalErrors
},
PlasmaCore.Action {
text: qsTr("About")
icon.name: "help-about"
onTriggered: Plasmoid.showAboutDialog()
}
]
PlasmaCore.Action {
id: configureAction
text: qsTr("Settings")
icon.name: "configure"
onTriggered: Plasmoid.showSettingsDlg()
}
Component.onCompleted: {
Plasmoid.initEngine(this)
Plasmoid.setInternalAction("configure", configureAction)
}
}

View File

@ -5,7 +5,7 @@ source "$script_dir/settestenv.sh"
# use the package dir within the source-tree so one does not need to run CMake again for updating
# build-tree copy all the time
package_dir=$script_dir/../package
package_dir=$script_dir/../$2
# copy the generated desktop file back into the source-tree package dir so it can actually be used
meta_data_file=$1

View File

@ -1,16 +1,15 @@
# Testing and debugging Plasma 5 plasmoid with Qt Creator
# Testing and debugging Plasmoid for Plasma 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
1. Build as usual, ensure `NO_PLASMOID` is turned off.
2. Add build step to execute the custom target `init_plasmoid_testing` which
will install the Plasmoid in a test directory which is `$CMAKE_BUILD_DIR/plasmoid-testing`
by default (configurable via cache variable `PLASMOID_TESTDIR`, the sub directory
`plasmoid-testing` is not part of the variable)
3. Add new config for run in Qt Creator and set `bash` as executable
`plasmoid-testing` is not part of the variable).
3. Add new config for run in Qt Creator and set `bash` as executable.
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 specify `org.kde.plasma.systemtray` as applet to test how the Plasmoid
looks like within the system tray plasmoid.
@ -21,12 +20,12 @@ rather than the regular home to separate testing from production.
already take care of setting the environment.
* The home directory is set in accordance with the directory used in step 2. but can be overridden
by setting `TEST_HOME`; make sure that `TEST_HOME` and the CMake variable `PLASMOID_TESTDIR` are
set in accordance
set in accordance.
* If not already set, `QT_PLUGIN_PATH` is set to `$CMAKE_CURRENT_BINARY_DIR/plasmoid/lib` which
should contain the plugin for the Plasmoid under `plasma/applets/libsyncthingplasmoid.so`
* Set `QT_DEBUG_PLUGINS` to 1 for verbose plugin detection
7. Ignore warning that executable is no debug build, it is sufficiant when
the plugin is a debug build (see next section for QML debugging)
should contain the plugin for the Plasmoid under `plasma/applets/libsyncthingplasmoid.so`.
* Set `QT_DEBUG_PLUGINS` to 1 for verbose plugin detection.
7. Ignore warning that executable is no debug build, it is sufficient when
the plugin is a debug build (see next section for QML debugging).
## Saving/restoring settings
@ -64,3 +63,18 @@ To create a debug build of `plasmoidviewer` manually:
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.
# Testing against a development build of Plasma
1. Build the whole dependency chain up to `plasma-desktop` installing it under some custom prefix.
Note that `plasma-sdk` alone is not sufficient.
2. Then follow the usual steps but make sure you build Syncthing Tray against the custom KDE builds.
This is achieved the easiest by using the `debug-kde` CMake preset. This preset uses the environment
variable `KDE_INSTALL_DIR` which must point to the custom prefix used in step 1.
3. Source the `prefix.sh` script that should be present in the build directory of any KDE library
you built in step 1, e.g. `source kde/plasma-sdk/prefix.sh`.
4. When setting the environment one needs to be more careful to not override variables set in step 3.
It is the easiest to just start e.g. `plasmawindowed` from the shell:
```
QT_PLUGIN_PATH=$BUILD_DIR/syncthingtray/debug-kde/syncthingtray/plasmoid/lib:$QT_PLUGIN_PATH HOME=$BUILD_DIR/syncthingtray/debug-kde/plasmoid-testing kdeinstall/bin/plasmawindowed martchus.syncthingplasmoid-devel
```
* It would make sense to tweak `starttesting.sh` to be able to do the sourcing automatically.