Use QML ListView in KCM

This commit is contained in:
Albert Astals Cid 2024-10-06 21:40:39 +00:00 committed by Albert Vaca Cintora
parent 5d9b8d966c
commit c0036d2d9b
9 changed files with 165 additions and 54 deletions

View file

@ -8,7 +8,7 @@ set (RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE
project(kdeconnect VERSION ${RELEASE_SERVICE_VERSION}) project(kdeconnect VERSION ${RELEASE_SERVICE_VERSION})
set(KF_MIN_VERSION "5.240") set(KF_MIN_VERSION "6.0.0")
set(QT_MIN_VERSION "6.7.0") set(QT_MIN_VERSION "6.7.0")
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
@ -94,7 +94,7 @@ ecm_find_qmlmodule(QtQuick.Particles 2.0)
add_definitions(-DQT_NO_URL_CAST_FROM_STRING -DQT_NO_KEYWORDS -DQT_NO_CAST_FROM_ASCII) add_definitions(-DQT_NO_URL_CAST_FROM_STRING -DQT_NO_KEYWORDS -DQT_NO_CAST_FROM_ASCII)
find_package(Qt6 ${QT_MIN_VERSION} REQUIRED COMPONENTS DBus Quick QuickControls2 Network Multimedia) find_package(Qt6 ${QT_MIN_VERSION} REQUIRED COMPONENTS DBus Quick QuickWidgets QuickControls2 Network Multimedia)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications
KIO KCMUtils Service Solid Kirigami People WindowSystem GuiAddons DocTools Crash) KIO KCMUtils Service Solid Kirigami People WindowSystem GuiAddons DocTools Crash)

View file

@ -57,6 +57,7 @@ Kirigami.ScrollablePage
text: i18nd("kdeconnect-app", "No devices found") text: i18nd("kdeconnect-app", "No devices found")
icon.name: 'edit-none-symbolic' icon.name: 'edit-none-symbolic'
anchors.centerIn: parent anchors.centerIn: parent
width: parent.width - (Kirigami.Units.largeSpacing * 4)
visible: devices.count === 0 visible: devices.count === 0
} }

View file

@ -1,7 +1,8 @@
qt_add_resources(kcm_SRCS assets.qrc)
add_definitions(-DTRANSLATION_DOMAIN="kdeconnect-kcm") add_definitions(-DTRANSLATION_DOMAIN="kdeconnect-kcm")
kcoreaddons_add_plugin(kcm_kdeconnect SOURCES kcm.cpp ${kcm_SRCS} INSTALL_NAMESPACE plasma/kcms/systemsettings_qwidgets)
kcoreaddons_add_plugin(kcm_kdeconnect SOURCES kcm.cpp INSTALL_NAMESPACE plasma/kcms/systemsettings_qwidgets)
kcmutils_generate_desktop_file(kcm_kdeconnect) kcmutils_generate_desktop_file(kcm_kdeconnect)
ki18n_wrap_ui(kcm_kdeconnect kcm.ui) ki18n_wrap_ui(kcm_kdeconnect kcm.ui)
@ -9,6 +10,7 @@ ki18n_wrap_ui(kcm_kdeconnect kcm.ui)
target_link_libraries(kcm_kdeconnect target_link_libraries(kcm_kdeconnect
Qt::Core Qt::Core
Qt::Gui Qt::Gui
Qt::QuickWidgets
KF6::I18n KF6::I18n
KF6::KCMUtils KF6::KCMUtils
kdeconnectinterfaces kdeconnectinterfaces

View file

@ -5,5 +5,6 @@ $XGETTEXT rc.cpp -o $podir/kdeconnect-kcm.pot
rm -f rc.cpp rm -f rc.cpp
#.cpp (-j passed to merge into existing file) #.cpp (-j passed to merge into existing file)
$XGETTEXT `find . -name '*.qml'` -j -o $podir/kdeconnect-kcm.pot
$XGETTEXT `find . -name '*.cpp'` -j -o $podir/kdeconnect-kcm.pot $XGETTEXT `find . -name '*.cpp'` -j -o $podir/kdeconnect-kcm.pot

7
kcm/assets.qrc Normal file
View file

@ -0,0 +1,7 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/kdeconnectkcm">
<file>list.qml</file>
</qresource>
</RCC>

View file

@ -14,6 +14,9 @@
#include <KPluginMetaData> #include <KPluginMetaData>
#include <kcmutils_version.h> #include <kcmutils_version.h>
#include <QQmlContext>
#include <QQuickItem>
#include "dbushelpers.h" #include "dbushelpers.h"
#include "dbusinterfaces.h" #include "dbusinterfaces.h"
#include "devicesmodel.h" #include "devicesmodel.h"
@ -22,6 +25,21 @@
K_PLUGIN_CLASS_WITH_JSON(KdeConnectKcm, "kcm_kdeconnect.json") K_PLUGIN_CLASS_WITH_JSON(KdeConnectKcm, "kcm_kdeconnect.json")
class QQuickWidgetPaleteChangeWatcher : public QObject
{
using QObject::QObject;
bool eventFilter(QObject *watched, QEvent *event) override
{
if (event->type() == QEvent::PaletteChange || event->type() == QEvent::ApplicationPaletteChange) {
// We know that watched is a QQuickWidget
QQuickWidget *w = static_cast<QQuickWidget *>(watched);
w->setClearColor(w->palette().color(QPalette::Window));
}
return QObject::eventFilter(watched, event);
}
};
KdeConnectKcm::KdeConnectKcm(QObject *parent, const KPluginMetaData &md, const QVariantList &args) KdeConnectKcm::KdeConnectKcm(QObject *parent, const KPluginMetaData &md, const QVariantList &args)
: KCModule(parent, md) : KCModule(parent, md)
, daemon(new DaemonDbusInterface(this)) , daemon(new DaemonDbusInterface(this))
@ -37,7 +55,14 @@ KdeConnectKcm::KdeConnectKcm(QObject *parent, const KPluginMetaData &md, const Q
sortProxyModel = new DevicesSortProxyModel(devicesModel); sortProxyModel = new DevicesSortProxyModel(devicesModel);
kcmUi.deviceList->setModel(sortProxyModel); kcmUi.list_quick_widget->setMinimumWidth(250 * kcmUi.list_quick_widget->devicePixelRatio());
kcmUi.list_quick_widget->rootContext()->setContextObject(new KLocalizedContext(kcmUi.list_quick_widget));
kcmUi.list_quick_widget->setClearColor(kcmUi.list_quick_widget->palette().color(QPalette::Window));
kcmUi.list_quick_widget->setSource(QUrl(QStringLiteral("qrc:/kdeconnectkcm/list.qml")));
kcmUi.list_quick_widget->rootObject()->setProperty("model", QVariant::fromValue(sortProxyModel));
connect(kcmUi.list_quick_widget->rootObject(), SIGNAL(clicked(QString)), this, SLOT(deviceSelected(QString)));
kcmUi.list_quick_widget->installEventFilter(new QQuickWidgetPaleteChangeWatcher(kcmUi.list_quick_widget));
kcmUi.deviceInfo->setVisible(false); kcmUi.deviceInfo->setVisible(false);
kcmUi.progressBar->setVisible(false); kcmUi.progressBar->setVisible(false);
@ -67,8 +92,6 @@ KdeConnectKcm::KdeConnectKcm(QObject *parent, const KPluginMetaData &md, const Q
setButtons(KCModule::Help | KCModule::NoAdditionalButton); setButtons(KCModule::Help | KCModule::NoAdditionalButton);
connect(devicesModel, &QAbstractItemModel::dataChanged, this, &KdeConnectKcm::resetSelection);
connect(kcmUi.deviceList->selectionModel(), &QItemSelectionModel::currentChanged, this, &KdeConnectKcm::deviceSelected);
connect(kcmUi.accept_button, &QAbstractButton::clicked, this, &KdeConnectKcm::acceptPairing); connect(kcmUi.accept_button, &QAbstractButton::clicked, this, &KdeConnectKcm::acceptPairing);
connect(kcmUi.reject_button, &QAbstractButton::clicked, this, &KdeConnectKcm::cancelPairing); connect(kcmUi.reject_button, &QAbstractButton::clicked, this, &KdeConnectKcm::cancelPairing);
connect(kcmUi.cancel_button, &QAbstractButton::clicked, this, &KdeConnectKcm::cancelPairing); connect(kcmUi.cancel_button, &QAbstractButton::clicked, this, &KdeConnectKcm::cancelPairing);
@ -88,11 +111,8 @@ KdeConnectKcm::KdeConnectKcm(QObject *parent, const KPluginMetaData &md, const Q
const QString pluginCM = colonIdx < 0 ? QString() : input.mid(colonIdx + 1); const QString pluginCM = colonIdx < 0 ? QString() : input.mid(colonIdx + 1);
connect(devicesModel, &DevicesModel::rowsInserted, this, [this, deviceId, pluginCM]() { connect(devicesModel, &DevicesModel::rowsInserted, this, [this, deviceId, pluginCM]() {
auto row = devicesModel->rowForDevice(deviceId); kcmUi.list_quick_widget->rootObject()->setProperty("currentDeviceId", deviceId);
if (row >= 0) { deviceSelected(deviceId);
const QModelIndex idx = sortProxyModel->mapFromSource(devicesModel->index(row));
kcmUi.deviceList->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
}
if (!pluginCM.isEmpty()) { if (!pluginCM.isEmpty()) {
kcmUi.pluginSelector->showConfiguration(pluginCM); kcmUi.pluginSelector->showConfiguration(pluginCM);
} }
@ -136,29 +156,19 @@ void KdeConnectKcm::refresh()
daemon->forceOnNetworkChange(); daemon->forceOnNetworkChange();
} }
void KdeConnectKcm::resetSelection() void KdeConnectKcm::deviceSelected(const QString &deviceId)
{
if (!currentDevice) {
return;
}
kcmUi.deviceList->selectionModel()->setCurrentIndex(sortProxyModel->mapFromSource(currentIndex), QItemSelectionModel::ClearAndSelect);
}
void KdeConnectKcm::deviceSelected(const QModelIndex &current)
{ {
if (currentDevice) { if (currentDevice) {
disconnect(currentDevice, nullptr, this, nullptr); disconnect(currentDevice, nullptr, this, nullptr);
} }
if (!current.isValid()) { currentDevice = devicesModel->getDevice(devicesModel->rowForDevice(deviceId));
if (!currentDevice) {
currentDevice = nullptr; currentDevice = nullptr;
kcmUi.deviceInfo->setVisible(false); kcmUi.deviceInfo->setVisible(false);
return; return;
} }
currentIndex = sortProxyModel->mapToSource(current);
currentDevice = devicesModel->getDevice(currentIndex.row());
kcmUi.noDevicePlaceholder->setVisible(false); kcmUi.noDevicePlaceholder->setVisible(false);
bool valid = (currentDevice != nullptr && currentDevice->isValid()); bool valid = (currentDevice != nullptr && currentDevice->isValid());
kcmUi.deviceInfo->setVisible(valid); kcmUi.deviceInfo->setVisible(valid);

View file

@ -30,11 +30,10 @@ private:
void save() override; void save() override;
private Q_SLOTS: private Q_SLOTS:
void deviceSelected(const QModelIndex &current); void deviceSelected(const QString &deviceId);
void requestPairing(); void requestPairing();
void pluginsConfigChanged(bool changed); void pluginsConfigChanged(bool changed);
void sendPing(); void sendPing();
void resetSelection();
void pairingFailed(const QString &error); void pairingFailed(const QString &error);
void refresh(); void refresh();
void renameShow(); void renameShow();
@ -53,7 +52,6 @@ private:
DevicesModel *devicesModel; DevicesModel *devicesModel;
DevicesSortProxyModel *sortProxyModel; DevicesSortProxyModel *sortProxyModel;
DeviceDbusInterface *currentDevice; DeviceDbusInterface *currentDevice;
QModelIndex currentIndex;
QStringList m_oldSupportedPluginNames; QStringList m_oldSupportedPluginNames;
public Q_SLOTS: public Q_SLOTS:

View file

@ -51,7 +51,6 @@
<property name="font"> <property name="font">
<font> <font>
<pointsize>12</pointsize> <pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -59,14 +58,14 @@
<string>KDE Connect</string> <string>KDE Connect</string>
</property> </property>
<property name="textFormat"> <property name="textFormat">
<enum>Qt::PlainText</enum> <enum>Qt::TextFormat::PlainText</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="horizontalSpacer_2"> <spacer name="horizontalSpacer_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Orientation::Horizontal</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
@ -82,8 +81,7 @@
<string>Edit</string> <string>Edit</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset theme="edit-rename"> <iconset theme="edit-rename"/>
<normaloff>.</normaloff>.</iconset>
</property> </property>
</widget> </widget>
</item> </item>
@ -104,15 +102,24 @@
<string>Save</string> <string>Save</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset theme="dialog-ok"> <iconset theme="dialog-ok"/>
<normaloff>.</normaloff>.</iconset>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QListView" name="deviceList"/> <widget class="QQuickWidget" name="list_quick_widget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="resizeMode">
<enum>QQuickWidget::ResizeMode::SizeRootObjectToView</enum>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="refresh_button"> <widget class="QPushButton" name="refresh_button">
@ -152,7 +159,7 @@
</property> </property>
<layout class="QHBoxLayout" name="header"> <layout class="QHBoxLayout" name="header">
<property name="sizeConstraint"> <property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum> <enum>QLayout::SizeConstraint::SetMaximumSize</enum>
</property> </property>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
@ -185,7 +192,6 @@
<property name="font"> <property name="font">
<font> <font>
<pointsize>10</pointsize> <pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -193,7 +199,7 @@
<string>Device</string> <string>Device</string>
</property> </property>
<property name="textFormat"> <property name="textFormat">
<enum>Qt::PlainText</enum> <enum>Qt::TextFormat::PlainText</enum>
</property> </property>
</widget> </widget>
</item> </item>
@ -213,7 +219,7 @@
<item> <item>
<spacer name="horizontalSpacer_3"> <spacer name="horizontalSpacer_3">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Orientation::Horizontal</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
@ -229,10 +235,10 @@
<item> <item>
<widget class="KSqueezedTextLabel" name="verificationKey"> <widget class="KSqueezedTextLabel" name="verificationKey">
<property name="text"> <property name="text">
<string>🔑 abababab</string> <string>KSqueezedTextLabel</string>
</property> </property>
<property name="textInteractionFlags"> <property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> <set>Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -241,7 +247,7 @@
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Orientation::Horizontal</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
@ -340,7 +346,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="KMessageWidget" name="messages" native="true"/> <widget class="KMessageWidget" name="messages"/>
</item> </item>
<item> <item>
<widget class="KPluginWidget" name="pluginSelector" native="true"> <widget class="KPluginWidget" name="pluginSelector" native="true">
@ -351,7 +357,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="focusPolicy"> <property name="focusPolicy">
<enum>Qt::WheelFocus</enum> <enum>Qt::FocusPolicy::WheelFocus</enum>
</property> </property>
</widget> </widget>
</item> </item>
@ -373,10 +379,10 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;No device selected.&lt;br&gt;&lt;br&gt;If you own an Android device, make sure to install the &lt;a href=&quot;https://play.google.com/store/apps/details?id=org.kde.kdeconnect_tp&quot;&gt;&lt;span style=&quot; text-decoration: underline;&quot;&gt;KDE Connect Android app&lt;/span&gt;&lt;/a&gt; (also available &lt;a href=&quot;https://f-droid.org/repository/browse/?fdid=org.kde.kdeconnect_tp&quot;&gt;&lt;span style=&quot; text-decoration: underline;&quot;&gt;from F-Droid&lt;/span&gt;&lt;/a&gt;) and it should appear in the list. If you have an iPhone, make sure to install the &lt;a href=&quot;https://apps.apple.com/us/app/kde-connect/id1580245991&quot;&gt;&lt;span style=&quot; text-decoration: underline;&quot;&gt;KDE Connect iOS app&lt;/span&gt;&lt;/a&gt; &lt;br&gt;&lt;br&gt;If you are having problems, visit the &lt;a href=&quot;https://userbase.kde.org/KDEConnect&quot;&gt;&lt;span style=&quot; text-decoration: underline;&quot;&gt;KDE Connect Community wiki&lt;/span&gt;&lt;/a&gt; for help.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;No device selected.&lt;br&gt;&lt;br&gt;If you own an Android device, make sure to install the &lt;a href=&quot;https://play.google.com/store/apps/details?id=org.kde.kdeconnect_tp&quot;&gt;&lt;span style=&quot; text-decoration: underline;&quot;&gt;KDE Connect Android app&lt;/span&gt;&lt;/a&gt; (also available &lt;a href=&quot;https://f-droid.org/repository/browse/?fdid=org.kde.kdeconnect_tp&quot;&gt;&lt;span style=&quot; text-decoration: underline;&quot;&gt;from F-Droid&lt;/span&gt;&lt;/a&gt;) and it should appear in the list. If you have an iPhone, make sure to install the &lt;a href=&quot;https://apps.apple.com/us/app/kde-connect/id1580245991&quot;&gt;&lt;span style=&quot; text-decoration: underline;&quot;&gt;KDE Connect iOS app&lt;/span&gt;&lt;/a&gt; &lt;br&gt;&lt;br&gt;If you are having problems, visit the &lt;a href=&quot;https://userbase.kde.org/KDEConnect&quot;&gt;&lt;span style=&quot; text-decoration: underline;&quot;&gt;KDE Connect Community wiki&lt;/span&gt;&lt;/a&gt; for help.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="textFormat"> <property name="textFormat">
<enum>Qt::RichText</enum> <enum>Qt::TextFormat::RichText</enum>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignCenter</set> <set>Qt::AlignmentFlag::AlignCenter</set>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
@ -388,7 +394,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="textInteractionFlags"> <property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set> <set>Qt::TextInteractionFlag::LinksAccessibleByKeyboard|Qt::TextInteractionFlag::LinksAccessibleByMouse</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -401,12 +407,6 @@
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget>
<class>KPluginWidget</class>
<extends>QWidget</extends>
<header>kpluginwidget.h</header>
<container>1</container>
</customwidget>
<customwidget> <customwidget>
<class>KMessageWidget</class> <class>KMessageWidget</class>
<extends>QFrame</extends> <extends>QFrame</extends>
@ -418,6 +418,17 @@
<extends>QLabel</extends> <extends>QLabel</extends>
<header>ksqueezedtextlabel.h</header> <header>ksqueezedtextlabel.h</header>
</customwidget> </customwidget>
<customwidget>
<class>QQuickWidget</class>
<extends>QWidget</extends>
<header location="global">QtQuickWidgets/QQuickWidget</header>
</customwidget>
<customwidget>
<class>KPluginWidget</class>
<extends>QWidget</extends>
<header>kpluginwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

81
kcm/list.qml Normal file
View file

@ -0,0 +1,81 @@
/*
* SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez <aleixpol@kde.org>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kdeconnect
ScrollView {
id: root
focus: true
signal clicked(string device)
property string currentDeviceId
property alias model: devices.model
Component.onCompleted: {
if (background) {
background.visible = true
}
}
ListView {
id: devices
focus: true
section {
property: "status"
delegate: Kirigami.ListSectionHeader {
width: ListView.view.width
text: switch (parseInt(section))
{
case DevicesModel.Paired:
return i18nd("kdeconnect-kcm", "Remembered")
case DevicesModel.Reachable:
return i18nd("kdeconnect-kcm", "Available")
case (DevicesModel.Reachable | DevicesModel.Paired):
return i18nd("kdeconnect-kcm", "Connected")
}
}
}
Kirigami.PlaceholderMessage {
text: i18nd("kdeconnect-kcm", "No devices found")
icon.name: 'edit-none-symbolic'
anchors.centerIn: parent
width: parent.width - (Kirigami.Units.largeSpacing * 4)
visible: devices.count === 0
}
delegate: ItemDelegate {
id: delegate
icon.name: iconName
text: model.name
width: ListView.view.width
highlighted: root.currentDeviceId === deviceId
focus: true
contentItem: Kirigami.IconTitleSubtitle {
title: delegate.text
subtitle: toolTip
icon: icon.fromControlsIcon(delegate.icon)
}
onClicked: {
root.currentDeviceId = deviceId
root.clicked(deviceId)
}
}
}
}