Commit a04b40da authored by Ismael Asensio's avatar Ismael Asensio

KWinRules KCM Redesign

Summary:
Replacement KCM to configure kwin rules, using a QML-based UI.

After some work on the task T12729, it is almost feature-par with the previous module, and adapted to the recent move to KConfigXT.

Test Plan:
{F8208046}
{F8208047}

Reviewers: #plasma, #kwin, #vdg, ngraham, davidedmundson, zzag

Reviewed By: #plasma, #kwin, #vdg, ngraham, davidedmundson, zzag

Subscribers: ngraham, davidedmundson, hchain, broulik, zzag, kwin

Tags: #kwin, #vdg

Differential Revision: https://phabricator.kde.org/D28152
parent 19bfa7c0
......@@ -3,20 +3,19 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kcmkwinrules\")
add_definitions(-DKCMRULES)
include_directories(../../)
set(kwinrules_SRCS ../../rulebooksettings.cpp
../../cursor.cpp
../../plugins/platforms/x11/standalone/x11cursor.cpp
../../rules.cpp
../../placement.cpp
../../utils.cpp
yesnobox.cpp
ruleswidget.cpp
ruleslist.cpp
kwinsrc.cpp
detectwidget.cpp
)
ki18n_wrap_ui(kwinrules_SRCS ruleslist.ui detectwidget.ui editshortcut.ui ruleswidgetbase.ui)
set(kwinrules_SRCS
../../rulebooksettings.cpp
../../cursor.cpp
../../plugins/platforms/x11/standalone/x11cursor.cpp
../../rules.cpp
../../placement.cpp
../../utils.cpp
kwinsrc.cpp
optionsmodel.cpp
ruleitem.cpp
rulesmodel.cpp
)
kconfig_add_kcfg_files(kwinrules_SRCS ../../rulesettings.kcfgc)
kconfig_add_kcfg_files(kwinrules_SRCS ../../rulebooksettingsbase.kcfgc)
......@@ -30,13 +29,11 @@ set(kwin_kcm_rules_XCB_LIBS
)
set(kcm_libs
Qt5::Concurrent
Qt5::X11Extras
Qt5::Quick
Qt5::QuickWidgets
KF5::Completion
KF5::ConfigWidgets
KF5::I18n
KF5::Service
KF5::QuickAddons
KF5::WindowSystem
KF5::XmlGui
)
......@@ -46,14 +43,14 @@ if (KWIN_BUILD_ACTIVITIES)
endif()
target_link_libraries(KWinRulesObjects ${kcm_libs} ${kwin_kcm_rules_XCB_LIBS})
add_executable(kwin_rules_dialog main.cpp)
add_executable(kwin_rules_dialog main.cpp rulesdialog.cpp)
target_link_libraries(kwin_rules_dialog KWinRulesObjects)
install(TARGETS kwin_rules_dialog DESTINATION ${LIBEXEC_INSTALL_DIR})
add_library(kcm_kwinrules MODULE kcm.cpp)
add_library(kcm_kwinrules MODULE kcmrules.cpp rulebookmodel.cpp)
target_link_libraries(kcm_kwinrules KWinRulesObjects)
install(TARGETS kcm_kwinrules DESTINATION ${PLUGIN_INSTALL_DIR})
########### install files ###############
kcoreaddons_desktop_to_json(kcm_kwinrules "kcm_kwinrules.desktop" SERVICE_TYPES kcmodule.desktop)
install(FILES kwinrules.desktop DESTINATION ${SERVICES_INSTALL_DIR})
install(TARGETS kcm_kwinrules DESTINATION ${PLUGIN_INSTALL_DIR}/kcms)
install(FILES kcm_kwinrules.desktop DESTINATION ${SERVICES_INSTALL_DIR})
kpackage_install_package(package kcm_kwinrules kcms)
/*
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "detectwidget.h"
#include "../../plugins/platforms/x11/standalone/x11cursor.h"
#include <KLocalizedString>
#include <QDebug>
#include <kwindowsystem.h>
#include <QLabel>
#include <QRadioButton>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QMouseEvent>
#include <QEvent>
#include <QByteArray>
#include <QTimer>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
Q_DECLARE_METATYPE(NET::WindowType)
namespace KWin
{
DetectWidget::DetectWidget(QWidget* parent)
: QWidget(parent)
{
setupUi(this);
}
DetectDialog::DetectDialog(QWidget* parent, const char* name)
: QDialog(parent)
{
setObjectName(name);
setModal(true);
setLayout(new QVBoxLayout);
widget = new DetectWidget(this);
layout()->addWidget(widget);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, this);
layout()->addWidget(buttons);
connect(buttons, SIGNAL(accepted()), SLOT(accept()));
connect(buttons, SIGNAL(rejected()), SLOT(reject()));
}
void DetectDialog::detect(int secs)
{
QTimer::singleShot(secs*1000, this, SLOT(selectWindow()));
}
void DetectDialog::executeDialog()
{
static const char* const types[] = {
I18N_NOOP("Normal Window"),
I18N_NOOP("Desktop"),
I18N_NOOP("Dock (panel)"),
I18N_NOOP("Toolbar"),
I18N_NOOP("Torn-Off Menu"),
I18N_NOOP("Dialog Window"),
I18N_NOOP("Override Type"),
I18N_NOOP("Standalone Menubar"),
I18N_NOOP("Utility Window"),
I18N_NOOP("Splash Screen")
};
widget->class_label->setText(wmclass_class + QLatin1String(" (") + wmclass_name + ' ' + wmclass_class + ')');
widget->role_label->setText(role);
widget->match_role->setEnabled(!role.isEmpty());
if (type == NET::Unknown)
widget->type_label->setText(i18n("Unknown - will be treated as Normal Window"));
else
widget->type_label->setText(i18n(types[ type ]));
widget->title_label->setText(title);
widget->machine_label->setText(machine);
widget->adjustSize();
adjustSize();
if (width() < 4*height()/3)
resize(4*height()/3, height());
emit detectionDone(exec() == QDialog::Accepted);
}
QByteArray DetectDialog::selectedClass() const
{
if (widget->match_whole_class->isChecked())
return wmclass_name + ' ' + wmclass_class;
return wmclass_class;
}
bool DetectDialog::selectedWholeClass() const
{
return widget->match_whole_class->isChecked();
}
QByteArray DetectDialog::selectedRole() const
{
if (widget->match_role->isChecked())
return role;
return "";
}
QString DetectDialog::selectedTitle() const
{
return title;
}
Rules::StringMatch DetectDialog::titleMatch() const
{
return widget->match_title->isChecked() ? Rules::ExactMatch : Rules::UnimportantMatch;
}
bool DetectDialog::selectedWholeApp() const
{
return !widget->match_type->isChecked();
}
NET::WindowType DetectDialog::selectedType() const
{
return type;
}
QByteArray DetectDialog::selectedMachine() const
{
return machine;
}
void DetectDialog::selectWindow()
{
QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"),
QStringLiteral("/KWin"),
QStringLiteral("org.kde.KWin"),
QStringLiteral("queryWindowInfo"));
QDBusPendingReply<QVariantMap> async = QDBusConnection::sessionBus().asyncCall(message);
QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this);
connect(callWatcher, &QDBusPendingCallWatcher::finished, this,
[this](QDBusPendingCallWatcher *self) {
QDBusPendingReply<QVariantMap> reply = *self;
self->deleteLater();
if (!reply.isValid()) {
emit detectionDone(false);
return;
}
m_windowInfo = reply.value();
wmclass_class = m_windowInfo.value("resourceClass").toByteArray();
wmclass_name = m_windowInfo.value("resourceName").toByteArray();
role = m_windowInfo.value("role").toByteArray();
type = m_windowInfo.value("type").value<NET::WindowType>();
title = m_windowInfo.value("caption").toString();
machine = m_windowInfo.value("clientMachine").toByteArray();
executeDialog();
}
);
}
} // namespace
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KWin::DetectWidget</class>
<widget class="QWidget" name="KWin::DetectWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>450</width>
<height>300</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Information About Selected Window</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="textLabel1">
<property name="text">
<string>Class:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="class_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="textLabel3">
<property name="text">
<string>Role:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="role_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="textLabel4">
<property name="text">
<string>Type:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="type_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="textLabel8">
<property name="text">
<string>Title:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="title_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="textLabel13">
<property name="text">
<string>Machine:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="machine_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Match by primary class name and</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="match_whole_class">
<property name="text">
<string>Secondary class name (resulting in term in brackets)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="match_role">
<property name="text">
<string>Window role (can be used to select windows by function)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="match_type">
<property name="text">
<string>Window type (eg. all dialogs, but not the main windows)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="match_title">
<property name="text">
<string>Window title (very specific, can fail due to content changes or translation)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
<ui version="4.0" >
<class>EditShortcut</class>
<widget class="QWidget" name="EditShortcut" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>1194</width>
<height>447</height>
</rect>
</property>
<layout class="QVBoxLayout" >
<item>
<widget class="QLabel" name="textLabel2" >
<property name="text" >
<string>A single shortcut can be easily assigned or cleared using the two buttons. Only shortcuts with modifiers can be used.&lt;p>
It is possible to have several possible shortcuts, and the first available shortcut will be used. The shortcuts are specified using shortcut sets separated by " - ". One set is specified as &lt;i>base&lt;/i>+(&lt;i>list&lt;/i>), where base are modifiers and list is a list of keys.&lt;br>
For example "&lt;b>Shift+Alt+(123) Shift+Ctrl+(ABC)&lt;/b>" will first try &lt;b>Shift+Alt+1&lt;/b>, then others with &lt;b>Shift+Ctrl+C&lt;/b> as the last one.</string>
</property>
<property name="textFormat" >
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line1" >
<property name="frameShape" >
<enum>QFrame::HLine</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Sunken</enum>
</property>
</widget>
</item>
<item>
<widget class="KLineEdit" native="1" name="shortcut" />
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton1" >
<property name="text" >
<string>&amp;Single Shortcut</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton2" >
<property name="text" >
<string>C&amp;lear</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line2" >
<property name="frameShape" >
<enum>QFrame::HLine</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Sunken</enum>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KLineEdit</class>
<extends>QWidget</extends>
<header>klineedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>pushButton1</sender>
<signal>clicked()</signal>
<receiver>EditShortcut</receiver>
<slot>editShortcut()</slot>
<hints>
<hint type="sourcelabel" >
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel" >
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>pushButton2</sender>
<signal>clicked()</signal>
<receiver>EditShortcut</receiver>
<slot>clearShortcut()</slot>
<hints>
<hint type="sourcelabel" >
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel" >
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections>
</ui>