File indexing completed on 2025-01-26 05:06:35
0001 /* 0002 SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "libinput_config.h" 0008 #include "../configcontainer.h" 0009 0010 #include <KAboutData> 0011 #include <KLocalizedContext> 0012 #include <KLocalizedString> 0013 #include <KMessageWidget> 0014 0015 #include <QMetaObject> 0016 #include <QQmlContext> 0017 #include <QQmlEngine> 0018 #include <QQmlProperty> 0019 #include <QQuickItem> 0020 #include <QQuickWidget> 0021 #include <QVBoxLayout> 0022 0023 #include "inputbackend.h" 0024 0025 static QVariant getDeviceList(InputBackend *backend) 0026 { 0027 return QVariant::fromValue(backend->getDevices().toList()); 0028 } 0029 0030 LibinputConfig::LibinputConfig(ConfigContainer *parent, InputBackend *backend) 0031 : ConfigPlugin(parent) 0032 { 0033 m_backend = backend; 0034 0035 m_initError = !m_backend->errorString().isNull(); 0036 0037 m_view = new QQuickWidget(this); 0038 0039 m_errorMessage = new KMessageWidget(this); 0040 m_errorMessage->setCloseButtonVisible(false); 0041 m_errorMessage->setWordWrap(true); 0042 m_errorMessage->setVisible(false); 0043 0044 QVBoxLayout *layout = new QVBoxLayout(parent->widget()); 0045 0046 layout->addWidget(m_errorMessage); 0047 layout->addWidget(m_view); 0048 parent->widget()->setLayout(layout); 0049 0050 m_view->setResizeMode(QQuickWidget::SizeRootObjectToView); 0051 m_view->setClearColor(Qt::transparent); 0052 m_view->setAttribute(Qt::WA_AlwaysStackOnTop); 0053 0054 m_view->rootContext()->setContextProperty("backend", m_backend); 0055 m_view->rootContext()->setContextProperty("deviceModel", getDeviceList(m_backend)); 0056 0057 QObject::connect(m_view, &QQuickWidget::statusChanged, [&](QQuickWidget::Status status) { 0058 if (status == QQuickWidget::Ready) { 0059 connect(m_view->rootObject(), SIGNAL(changeSignal()), this, SLOT(onChange())); 0060 } 0061 }); 0062 0063 m_view->engine()->rootContext()->setContextObject(new KLocalizedContext(m_view->engine())); 0064 0065 #if BUILD_KCM_MOUSE_X11 0066 bool deviceless = m_backend->mode() == InputBackendMode::XLibinput; 0067 #else 0068 bool deviceless = false; 0069 #endif 0070 if (deviceless) { 0071 m_view->setSource(QUrl(QStringLiteral("qrc:/libinput/main_deviceless.qml"))); 0072 } else { 0073 m_view->setSource(QUrl(QStringLiteral("qrc:/libinput/main.qml"))); 0074 } 0075 0076 if (m_initError) { 0077 m_errorMessage->setMessageType(KMessageWidget::Error); 0078 m_errorMessage->setText(m_backend->errorString()); 0079 QMetaObject::invokeMethod(m_errorMessage, "animatedShow", Qt::QueuedConnection); 0080 } else { 0081 connect(m_backend, SIGNAL(deviceAdded(bool)), this, SLOT(onDeviceAdded(bool))); 0082 connect(m_backend, SIGNAL(deviceRemoved(int)), this, SLOT(onDeviceRemoved(int))); 0083 } 0084 0085 // Just set it to a reasonable default size 0086 // This only matters for kcmshell and most of the time we have the KCM in systemsettings open 0087 m_view->resize(QSize(300, 300)); 0088 m_view->show(); 0089 } 0090 0091 void LibinputConfig::load() 0092 { 0093 // in case of critical init error in backend, don't try 0094 if (m_initError) { 0095 return; 0096 } 0097 0098 if (!m_backend->getConfig()) { 0099 m_errorMessage->setMessageType(KMessageWidget::Error); 0100 m_errorMessage->setText(i18n("Error while loading values. See logs for more information. Please restart this configuration module.")); 0101 m_errorMessage->animatedShow(); 0102 } else { 0103 if (!m_backend->deviceCount()) { 0104 m_errorMessage->setMessageType(KMessageWidget::Information); 0105 m_errorMessage->setText(i18n("No pointer device found. Connect now.")); 0106 m_errorMessage->animatedShow(); 0107 } 0108 } 0109 QMetaObject::invokeMethod(m_view->rootObject(), "syncValuesFromBackend"); 0110 } 0111 0112 void LibinputConfig::save() 0113 { 0114 if (!m_backend->applyConfig()) { 0115 m_errorMessage->setMessageType(KMessageWidget::Error); 0116 m_errorMessage->setText(i18n("Not able to save all changes. See logs for more information. Please restart this configuration module and try again.")); 0117 m_errorMessage->animatedShow(); 0118 } else { 0119 hideErrorMessage(); 0120 } 0121 // load newly written values 0122 load(); 0123 // in case of error, config still in changed state 0124 m_parent->setNeedsSave(m_backend->isChangedConfig()); 0125 } 0126 0127 void LibinputConfig::defaults() 0128 { 0129 // in case of critical init error in backend, don't try 0130 if (m_initError) { 0131 return; 0132 } 0133 0134 if (!m_backend->getDefaultConfig()) { 0135 m_errorMessage->setMessageType(KMessageWidget::Error); 0136 m_errorMessage->setText(i18n("Error while loading default values. Failed to set some options to their default values.")); 0137 m_errorMessage->animatedShow(); 0138 } 0139 QMetaObject::invokeMethod(m_view->rootObject(), "syncValuesFromBackend"); 0140 m_parent->setNeedsSave(m_backend->isChangedConfig()); 0141 } 0142 0143 void LibinputConfig::onChange() 0144 { 0145 if (!m_backend->deviceCount()) { 0146 return; 0147 } 0148 hideErrorMessage(); 0149 m_parent->setNeedsSave(m_backend->isChangedConfig()); 0150 } 0151 0152 void LibinputConfig::onDeviceAdded(bool success) 0153 { 0154 QQuickItem *rootObj = m_view->rootObject(); 0155 0156 if (!success) { 0157 m_errorMessage->setMessageType(KMessageWidget::Error); 0158 m_errorMessage->setText(i18n("Error while adding newly connected device. Please reconnect it and restart this configuration module.")); 0159 } 0160 0161 int activeIndex; 0162 if (m_backend->deviceCount() == 1) { 0163 // if no pointer device was connected previously, show the new device and hide the no-device-message 0164 activeIndex = 0; 0165 hideErrorMessage(); 0166 } else { 0167 activeIndex = QQmlProperty::read(rootObj, "deviceIndex").toInt(); 0168 } 0169 m_view->rootContext()->setContextProperty("deviceModel", getDeviceList(m_backend)); 0170 QMetaObject::invokeMethod(rootObj, "resetModel", Q_ARG(QVariant, activeIndex)); 0171 QMetaObject::invokeMethod(rootObj, "syncValuesFromBackend"); 0172 } 0173 0174 void LibinputConfig::onDeviceRemoved(int index) 0175 { 0176 QQuickItem *rootObj = m_view->rootObject(); 0177 0178 int activeIndex = QQmlProperty::read(rootObj, "deviceIndex").toInt(); 0179 if (activeIndex == index) { 0180 m_errorMessage->setMessageType(KMessageWidget::Information); 0181 if (m_backend->deviceCount()) { 0182 m_errorMessage->setText(i18n("Pointer device disconnected. Closed its setting dialog.")); 0183 } else { 0184 m_errorMessage->setText(i18n("Pointer device disconnected. No other devices found.")); 0185 } 0186 m_errorMessage->animatedShow(); 0187 activeIndex = 0; 0188 } else { 0189 if (index < activeIndex) { 0190 activeIndex--; 0191 } 0192 } 0193 m_view->rootContext()->setContextProperty("deviceModel", getDeviceList(m_backend)); 0194 QMetaObject::invokeMethod(m_view->rootObject(), "resetModel", Q_ARG(QVariant, activeIndex)); 0195 QMetaObject::invokeMethod(rootObj, "syncValuesFromBackend"); 0196 0197 m_parent->setNeedsSave(m_backend->isChangedConfig()); 0198 } 0199 0200 void LibinputConfig::hideErrorMessage() 0201 { 0202 if (m_errorMessage->isVisible()) { 0203 m_errorMessage->animatedHide(); 0204 } 0205 }