Add support for custom resolutions
Add build-time check for qt6-quick3d
This commit is contained in:
parent
645a38176e
commit
5ec52baa3f
|
|
@ -4,10 +4,6 @@ project(breezy_desktop VERSION 0.0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
||||||
|
|
||||||
include(cmake/default-vars.cmake)
|
include(cmake/default-vars.cmake)
|
||||||
|
|
||||||
find_package(ECM "5.100" REQUIRED NO_MODULE)
|
find_package(ECM "5.100" REQUIRED NO_MODULE)
|
||||||
|
|
@ -16,16 +12,13 @@ set(CMAKE_MODULE_PATH
|
||||||
${ECM_MODULE_PATH}
|
${ECM_MODULE_PATH}
|
||||||
${ECM_KDE_MODULE_DIR}
|
${ECM_KDE_MODULE_DIR}
|
||||||
)
|
)
|
||||||
include(cmake/qtversion.cmake)
|
|
||||||
include(FeatureSummary)
|
include(FeatureSummary)
|
||||||
include(KDEInstallDirs)
|
include(KDEInstallDirs)
|
||||||
include(KDECMakeSettings)
|
include(KDECMakeSettings)
|
||||||
include(KDECompilerSettings NO_POLICY_SCOPE)
|
include(KDECompilerSettings NO_POLICY_SCOPE)
|
||||||
|
|
||||||
include(cmake/qtversion.cmake)
|
|
||||||
|
|
||||||
# required frameworks by Core
|
# required frameworks by Core
|
||||||
find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS
|
find_package(KF6 REQUIRED COMPONENTS
|
||||||
Config
|
Config
|
||||||
ConfigWidgets
|
ConfigWidgets
|
||||||
CoreAddons
|
CoreAddons
|
||||||
|
|
@ -36,16 +29,25 @@ find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS
|
||||||
XmlGui
|
XmlGui
|
||||||
)
|
)
|
||||||
|
|
||||||
if(${QT_MAJOR_VERSION} EQUAL 6)
|
find_package(KWin REQUIRED COMPONENTS kwineffects)
|
||||||
find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS KCMUtils)
|
message(STATUS "Found KWin Version: ${KWin_VERSION}")
|
||||||
find_package(KWin REQUIRED COMPONENTS kwineffects)
|
|
||||||
message(STATUS "Found KWin Version: ${KWin_VERSION}")
|
|
||||||
endif ()
|
|
||||||
include(cmake/info.cmake)
|
include(cmake/info.cmake)
|
||||||
find_package(epoxy REQUIRED)
|
find_package(epoxy REQUIRED)
|
||||||
find_package(XCB REQUIRED COMPONENTS XCB)
|
find_package(XCB REQUIRED COMPONENTS XCB)
|
||||||
find_package(KWinDBusInterface CONFIG REQUIRED)
|
find_package(KWinDBusInterface CONFIG REQUIRED)
|
||||||
|
|
||||||
|
find_library(QT6_QUICK3D_LIB
|
||||||
|
NAMES Qt6Quick3D Qt6Quick3D.so Qt6Quick3D.so.6
|
||||||
|
PATH_SUFFIXES lib
|
||||||
|
)
|
||||||
|
if(NOT QT6_QUICK3D_LIB)
|
||||||
|
message(FATAL_ERROR "Qt6 Quick3D runtime library not found (QtQuick3D). Please install the Qt6 Quick3D runtime.")
|
||||||
|
endif()
|
||||||
|
set_package_properties(Qt6Quick3D PROPERTIES
|
||||||
|
TYPE RUNTIME
|
||||||
|
PURPOSE "Required at runtime for 3D rendering (Qt Quick 3D)."
|
||||||
|
)
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
ki18n_install(po)
|
ki18n_install(po)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
if(${KF_MIN_VERSION} EQUAL 6)
|
set(KWIN_EFFECT_INCLUDE_FILE "/usr/include/kwin/effect/effect.h")
|
||||||
set(KWIN_EFFECT_INCLUDE_FILE "/usr/include/kwin/effect/effect.h")
|
|
||||||
else ()
|
|
||||||
set(KWIN_EFFECT_INCLUDE_FILE "/usr/include/kwineffects.h")
|
|
||||||
endif ()
|
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND sh -c "grep '#define KWIN_EFFECT_API_VERSION_MINOR' ${KWIN_EFFECT_INCLUDE_FILE} | awk '{print \$NF}'"
|
COMMAND sh -c "grep '#define KWIN_EFFECT_API_VERSION_MINOR' ${KWIN_EFFECT_INCLUDE_FILE} | awk '{print \$NF}'"
|
||||||
OUTPUT_VARIABLE KWIN_EFFECT_API_VERSION_MINOR OUTPUT_STRIP_TRAILING_WHITESPACE
|
OUTPUT_VARIABLE KWIN_EFFECT_API_VERSION_MINOR OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
find_package(KF6 QUIET COMPONENTS ConfigWidgets)
|
|
||||||
|
|
||||||
if(${KF6_FOUND} EQUAL 0)
|
|
||||||
set(QT_MIN_VERSION "5.15")
|
|
||||||
set(QT_MAJOR_VERSION 5)
|
|
||||||
set(KF_MIN_VERSION "5.78")
|
|
||||||
endif ()
|
|
||||||
|
|
@ -1,5 +1,14 @@
|
||||||
set(breezy_desktop_config_SOURCES breezydesktopeffectkcm.cpp labeledslider.cpp)
|
set(breezy_desktop_config_SOURCES
|
||||||
ki18n_wrap_ui(breezy_desktop_config_SOURCES breezydesktopeffectkcm.ui)
|
breezydesktopeffectkcm.cpp
|
||||||
|
labeledslider.cpp
|
||||||
|
customresolutiondialog.cpp
|
||||||
|
virtualdisplayrow.cpp
|
||||||
|
)
|
||||||
|
ki18n_wrap_ui(breezy_desktop_config_SOURCES
|
||||||
|
breezydesktopeffectkcm.ui
|
||||||
|
customresolutiondialog.ui
|
||||||
|
virtualdisplayrow.ui
|
||||||
|
)
|
||||||
qt_add_dbus_interface(breezy_desktop_config_SOURCES ${KWIN_EFFECTS_INTERFACE} kwineffects_interface)
|
qt_add_dbus_interface(breezy_desktop_config_SOURCES ${KWIN_EFFECTS_INTERFACE} kwineffects_interface)
|
||||||
|
|
||||||
kcoreaddons_add_plugin(breezy_desktop_config INSTALL_NAMESPACE "plasma/kcms" SOURCES ${breezy_desktop_config_SOURCES})
|
kcoreaddons_add_plugin(breezy_desktop_config INSTALL_NAMESPACE "plasma/kcms" SOURCES ${breezy_desktop_config_SOURCES})
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
#include "breezydesktopconfig.h"
|
#include "breezydesktopconfig.h"
|
||||||
#include "labeledslider.h"
|
#include "labeledslider.h"
|
||||||
#include "xrdriveripc.h"
|
#include "xrdriveripc.h"
|
||||||
|
#include "customresolutiondialog.h"
|
||||||
|
#include "virtualdisplayrow.h"
|
||||||
|
|
||||||
#include <kwineffects_interface.h>
|
#include <kwineffects_interface.h>
|
||||||
|
|
||||||
|
|
@ -11,6 +13,7 @@
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
#include <KConfigWatcher>
|
#include <KConfigWatcher>
|
||||||
#include <KSharedConfig>
|
#include <KSharedConfig>
|
||||||
|
#include <KConfigGroup>
|
||||||
#include <KPluginFactory>
|
#include <KPluginFactory>
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
|
@ -35,11 +38,150 @@
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QTabWidget>
|
#include <QTabWidget>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QSize>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QSlider>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(KWIN_XR, "kwin.xr")
|
Q_LOGGING_CATEGORY(KWIN_XR, "kwin.xr")
|
||||||
|
|
||||||
static const char EFFECT_GROUP[] = "Effect-breezy_desktop";
|
static const char EFFECT_GROUP[] = "Effect-breezy_desktop";
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Roles for QComboBox items
|
||||||
|
constexpr int ROLE_SIZE = Qt::UserRole + 1; // QVariant::fromValue(QSize)
|
||||||
|
constexpr int ROLE_IS_CUSTOM = Qt::UserRole + 2; // bool
|
||||||
|
constexpr int ROLE_IS_ADD_CUSTOM = Qt::UserRole + 3; // bool
|
||||||
|
|
||||||
|
QString stateDirPath()
|
||||||
|
{
|
||||||
|
const QString fallback = QDir::homePath() + QStringLiteral("/.local/state");
|
||||||
|
const QString base = qEnvironmentVariable("XDG_STATE_HOME", fallback);
|
||||||
|
return QDir::cleanPath(base + QStringLiteral("/breezy_kwin"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString customResolutionsFilePath()
|
||||||
|
{
|
||||||
|
return stateDirPath() + QStringLiteral("/custom_resolutions.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList loadCustomResolutions()
|
||||||
|
{
|
||||||
|
QFile f(customResolutionsFilePath());
|
||||||
|
if (!f.exists()) return {};
|
||||||
|
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) return {};
|
||||||
|
const QByteArray data = f.readAll();
|
||||||
|
f.close();
|
||||||
|
const QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||||
|
if (!doc.isArray()) return {};
|
||||||
|
QStringList out;
|
||||||
|
const QJsonArray arr = doc.array();
|
||||||
|
for (const QJsonValue &v : arr) {
|
||||||
|
if (!v.isString()) continue;
|
||||||
|
const QString s = v.toString().trimmed();
|
||||||
|
if (s.isEmpty()) continue;
|
||||||
|
if (!out.contains(s)) out << s; // dedupe while reading to keep UI clean
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveCustomResolutions(const QStringList &list)
|
||||||
|
{
|
||||||
|
QDir().mkpath(stateDirPath());
|
||||||
|
QFile f(customResolutionsFilePath());
|
||||||
|
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) return;
|
||||||
|
QJsonArray arr;
|
||||||
|
for (const QString &s : list) arr.push_back(s);
|
||||||
|
const QJsonDocument doc(arr);
|
||||||
|
f.write(doc.toJson(QJsonDocument::Compact));
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parseResString(const QString &text, int &w, int &h)
|
||||||
|
{
|
||||||
|
const QString t = text.trimmed().toLower();
|
||||||
|
const QString xChar = QString::fromUtf8("x");
|
||||||
|
const QString multChar = QString::fromUtf8("×");
|
||||||
|
QString s = t;
|
||||||
|
s.replace(multChar, xChar);
|
||||||
|
const QStringList parts = s.split(QLatin1Char('x'), Qt::SkipEmptyParts);
|
||||||
|
if (parts.size() != 2) return false;
|
||||||
|
bool okW=false, okH=false;
|
||||||
|
int ww = parts[0].toInt(&okW);
|
||||||
|
int hh = parts[1].toInt(&okH);
|
||||||
|
if (!okW || !okH) return false;
|
||||||
|
if (ww < 320 || hh < 200) return false;
|
||||||
|
if (ww > 32768 || hh > 32768) return false;
|
||||||
|
w = ww; h = hh; return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addResolutionItem(QComboBox *combo, QString label, QSize resolution, bool isCustom, bool isAddCustom) {
|
||||||
|
combo->addItem(label);
|
||||||
|
combo->setItemData(combo->count()-1, QVariant::fromValue(resolution), ROLE_SIZE);
|
||||||
|
combo->setItemData(combo->count()-1, isCustom, ROLE_IS_CUSTOM);
|
||||||
|
combo->setItemData(combo->count()-1, isAddCustom, ROLE_IS_ADD_CUSTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
void populateResolutionCombo(QComboBox *combo, const QStringList &custom)
|
||||||
|
{
|
||||||
|
if (!combo) return;
|
||||||
|
combo->clear();
|
||||||
|
|
||||||
|
addResolutionItem(combo, QStringLiteral("1080p"), QSize(1920,1080), false, false);
|
||||||
|
addResolutionItem(combo, QStringLiteral("1440p"), QSize(2560,1440), false, false);
|
||||||
|
|
||||||
|
for (const QString &res : custom) {
|
||||||
|
int w=0,h=0;
|
||||||
|
if (!parseResString(res, w, h)) continue;
|
||||||
|
const QString label = QStringLiteral("%1x%2").arg(w).arg(h);
|
||||||
|
addResolutionItem(combo, label, QSize(w,h), true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
addResolutionItem(combo, QObject::tr("Add custom…"), QSize(), false, true);
|
||||||
|
|
||||||
|
combo->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCustomIndex(const QComboBox *combo, int index)
|
||||||
|
{
|
||||||
|
if (!combo || index < 0 || index >= combo->count()) return false;
|
||||||
|
return combo->itemData(index, ROLE_IS_CUSTOM).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAddCustomIndex(const QComboBox *combo, int index)
|
||||||
|
{
|
||||||
|
if (!combo || index < 0 || index >= combo->count()) return false;
|
||||||
|
return combo->itemData(index, ROLE_IS_ADD_CUSTOM).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize sizeForIndex(const QComboBox *combo, int index)
|
||||||
|
{
|
||||||
|
if (!combo || index < 0 || index >= combo->count()) return {};
|
||||||
|
QVariant v = combo->itemData(index, ROLE_SIZE);
|
||||||
|
if (!v.isValid()) return {};
|
||||||
|
return v.toSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool showCustomResolutionDialog(QWidget *parent, int &outW, int &outH)
|
||||||
|
{
|
||||||
|
CustomResolutionDialog dlg(parent);
|
||||||
|
const int res = dlg.exec();
|
||||||
|
if (res == QDialog::Accepted) {
|
||||||
|
outW = dlg.widthValue();
|
||||||
|
outH = dlg.heightValue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void addShortcutAction(KActionCollection *collection, const BreezyShortcuts::Shortcut &shortcut)
|
void addShortcutAction(KActionCollection *collection, const BreezyShortcuts::Shortcut &shortcut)
|
||||||
{
|
{
|
||||||
QAction *action = collection->addAction(shortcut.actionName);
|
QAction *action = collection->addAction(shortcut.actionName);
|
||||||
|
|
@ -76,6 +218,76 @@ BreezyDesktopEffectConfig::BreezyDesktopEffectConfig(QObject *parent, const KPlu
|
||||||
chk->setVisible(true);
|
chk->setVisible(true);
|
||||||
chk->setEnabled(true);
|
chk->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize the resolution picker controls
|
||||||
|
if (auto combo = widget()->findChild<QComboBox*>(QStringLiteral("comboAddVirtualDisplay"))) {
|
||||||
|
QStringList custom = loadCustomResolutions();
|
||||||
|
populateResolutionCombo(combo, custom);
|
||||||
|
|
||||||
|
auto removeBtn = widget()->findChild<QPushButton*>(QStringLiteral("buttonRemoveCustomResolution"));
|
||||||
|
auto addBtn = widget()->findChild<QPushButton*>(QStringLiteral("buttonAddVirtualDisplay"));
|
||||||
|
|
||||||
|
combo->setProperty("lastResIndex", 0);
|
||||||
|
|
||||||
|
auto updateRemoveUi = [combo, removeBtn, addBtn]() {
|
||||||
|
if (!removeBtn) return;
|
||||||
|
const bool customSel = isCustomIndex(combo, combo->currentIndex());
|
||||||
|
removeBtn->setEnabled(customSel);
|
||||||
|
removeBtn->setVisible(customSel);
|
||||||
|
if (addBtn) addBtn->setEnabled(!isAddCustomIndex(combo, combo->currentIndex()));
|
||||||
|
};
|
||||||
|
|
||||||
|
connect(combo, qOverload<int>(&QComboBox::currentIndexChanged), this, [this, combo, updateRemoveUi]() {
|
||||||
|
const int idx = combo->currentIndex();
|
||||||
|
if (isAddCustomIndex(combo, idx)) {
|
||||||
|
const int oldIdx = combo->property("lastResIndex").toInt();
|
||||||
|
int w = 1920, h = 1080;
|
||||||
|
if (showCustomResolutionDialog(widget(), w, h)) {
|
||||||
|
const QString label = QStringLiteral("%1x%2").arg(w).arg(h);
|
||||||
|
QStringList custom = loadCustomResolutions();
|
||||||
|
if (!custom.contains(label)) {
|
||||||
|
custom << label;
|
||||||
|
saveCustomResolutions(custom);
|
||||||
|
}
|
||||||
|
populateResolutionCombo(combo, custom);
|
||||||
|
const int newIndex = combo->findText(label);
|
||||||
|
if (newIndex >= 0) combo->setCurrentIndex(newIndex);
|
||||||
|
combo->setProperty("lastResIndex", combo->currentIndex());
|
||||||
|
} else {
|
||||||
|
// Revert to previous selection if dialog cancelled
|
||||||
|
combo->setCurrentIndex(oldIdx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
combo->setProperty("lastResIndex", idx);
|
||||||
|
}
|
||||||
|
updateRemoveUi();
|
||||||
|
});
|
||||||
|
updateRemoveUi();
|
||||||
|
|
||||||
|
if (removeBtn) {
|
||||||
|
connect(removeBtn, &QPushButton::clicked, this, [this, combo]() {
|
||||||
|
const int idx = combo->currentIndex();
|
||||||
|
if (!isCustomIndex(combo, idx)) return;
|
||||||
|
const QString label = combo->itemText(idx);
|
||||||
|
QStringList custom = loadCustomResolutions();
|
||||||
|
custom.removeAll(label);
|
||||||
|
saveCustomResolutions(custom);
|
||||||
|
populateResolutionCombo(combo, custom);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (addBtn) {
|
||||||
|
connect(addBtn, &QPushButton::clicked, this, [this, combo]() {
|
||||||
|
const int idx = combo->currentIndex();
|
||||||
|
const QSize sz = sizeForIndex(combo, idx);
|
||||||
|
if (sz.isValid()) {
|
||||||
|
auto list = dbusAddVirtualDisplay(sz.width(), sz.height());
|
||||||
|
renderVirtualDisplays(list);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_statePollTimer.setInterval(2000);
|
m_statePollTimer.setInterval(2000);
|
||||||
|
|
@ -171,19 +383,7 @@ BreezyDesktopEffectConfig::BreezyDesktopEffectConfig(QObject *parent, const KPlu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wire Add Virtual Display buttons via DBus to the effect
|
// Resolution picker wiring handled above in Wayland section
|
||||||
if (auto btn1080p = widget()->findChild<QPushButton*>("buttonAdd1080p")) {
|
|
||||||
connect(btn1080p, &QPushButton::clicked, this, [this]() {
|
|
||||||
auto list = dbusAddVirtualDisplay(1920, 1080);
|
|
||||||
renderVirtualDisplays(list);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (auto btn1440p = widget()->findChild<QPushButton*>("buttonAdd1440p")) {
|
|
||||||
connect(btn1440p, &QPushButton::clicked, this, [this]() {
|
|
||||||
auto list = dbusAddVirtualDisplay(2560, 1440);
|
|
||||||
renderVirtualDisplays(list);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (auto lookAheadOverrideSlider = widget()->findChild<LabeledSlider*>("kcfg_LookAheadOverride")) {
|
if (auto lookAheadOverrideSlider = widget()->findChild<LabeledSlider*>("kcfg_LookAheadOverride")) {
|
||||||
lookAheadOverrideSlider->setValueText(-1, i18n("Default"));
|
lookAheadOverrideSlider->setValueText(-1, i18n("Default"));
|
||||||
}
|
}
|
||||||
|
|
@ -381,38 +581,12 @@ void BreezyDesktopEffectConfig::renderVirtualDisplays(const QVariantList &rows)
|
||||||
const int w = unwrapValue(row.value(QStringLiteral("width"))).toInt();
|
const int w = unwrapValue(row.value(QStringLiteral("width"))).toInt();
|
||||||
const int h = unwrapValue(row.value(QStringLiteral("height"))).toInt();
|
const int h = unwrapValue(row.value(QStringLiteral("height"))).toInt();
|
||||||
|
|
||||||
QWidget *rowWidget = new QWidget(listContainer);
|
auto *rowWidget = new VirtualDisplayRow(listContainer);
|
||||||
auto *hl = new QHBoxLayout(rowWidget);
|
rowWidget->setInfo(id, w, h);
|
||||||
hl->setContentsMargins(0, 0, 0, 0);
|
connect(rowWidget, &VirtualDisplayRow::removeRequested, this, [this](const QString &vid) {
|
||||||
|
auto list = dbusRemoveVirtualDisplay(vid);
|
||||||
auto *spacer = new QWidget(rowWidget);
|
|
||||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
|
||||||
hl->addWidget(spacer, 1);
|
|
||||||
|
|
||||||
auto *icon = new QLabel(rowWidget);
|
|
||||||
icon->setPixmap(QIcon::fromTheme(QStringLiteral("video-display-symbolic")).pixmap(16, 16));
|
|
||||||
icon->setContentsMargins(8, 0, 8, 0);
|
|
||||||
hl->addWidget(icon, 0);
|
|
||||||
|
|
||||||
auto *idLabel = new QLabel(QStringLiteral("%1").arg(id), rowWidget);
|
|
||||||
idLabel->setContentsMargins(8, 0, 8, 0);
|
|
||||||
hl->addWidget(idLabel, 0);
|
|
||||||
|
|
||||||
auto *resLabel = new QLabel(QStringLiteral("%1x%2").arg(w).arg(h), rowWidget);
|
|
||||||
resLabel->setContentsMargins(8, 0, 8, 0);
|
|
||||||
hl->addWidget(resLabel, 0);
|
|
||||||
|
|
||||||
auto *removeBtn = new QPushButton(rowWidget);
|
|
||||||
removeBtn->setIcon(QIcon::fromTheme(QStringLiteral("user-trash-symbolic")));
|
|
||||||
removeBtn->setToolTip(QStringLiteral("Remove virtual display"));
|
|
||||||
removeBtn->setObjectName(QStringLiteral("remove-virtual-display"));
|
|
||||||
hl->addWidget(removeBtn, 0);
|
|
||||||
|
|
||||||
connect(removeBtn, &QPushButton::clicked, this, [this, id]() {
|
|
||||||
auto list = dbusRemoveVirtualDisplay(id);
|
|
||||||
renderVirtualDisplays(list);
|
renderVirtualDisplays(list);
|
||||||
});
|
});
|
||||||
|
|
||||||
listLayout->addWidget(rowWidget);
|
listLayout->addWidget(rowWidget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -219,17 +219,47 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="layoutVirtualDisplayButtons">
|
<layout class="QHBoxLayout" name="layoutVirtualDisplayButtons">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="buttonAdd1080p">
|
<widget class="QComboBox" name="comboAddVirtualDisplay">
|
||||||
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>+ 1080p</string>
|
<string>1080p</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>1440p</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Add custom…</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="buttonRemoveCustomResolution">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Remove custom resolution</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="list-remove-symbolic"/>
|
||||||
|
</property>
|
||||||
|
<property name="flat"><bool>true</bool></property>
|
||||||
|
<property name="visible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="buttonAdd1440p">
|
<widget class="QPushButton" name="buttonAddVirtualDisplay">
|
||||||
<property name="text">
|
<property name="icon">
|
||||||
<string>+ 1440p</string>
|
<iconset theme="list-add-symbolic"/>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="flat"><bool>true</bool></property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include "customresolutiondialog.h"
|
||||||
|
#include "ui_customresolutiondialog.h"
|
||||||
|
|
||||||
|
CustomResolutionDialog::CustomResolutionDialog(QWidget *parent)
|
||||||
|
: QDialog(parent), ui(new Ui::CustomResolutionDialog)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomResolutionDialog::~CustomResolutionDialog() {
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CustomResolutionDialog::widthValue() const {
|
||||||
|
return ui->sliderWidth->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CustomResolutionDialog::heightValue() const {
|
||||||
|
return ui->sliderHeight->value();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
namespace Ui { class CustomResolutionDialog; }
|
||||||
|
|
||||||
|
class CustomResolutionDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit CustomResolutionDialog(QWidget *parent = nullptr);
|
||||||
|
~CustomResolutionDialog() override;
|
||||||
|
|
||||||
|
int widthValue() const;
|
||||||
|
int heightValue() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::CustomResolutionDialog *ui;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,209 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>CustomResolutionDialog</class>
|
||||||
|
<widget class="QDialog" name="CustomResolutionDialog">
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Add custom resolution</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="labelWidth">
|
||||||
|
<property name="text">
|
||||||
|
<string>Width</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QWidget" name="widthRow" native="true">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayoutWidth">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="sliderWidth">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>640</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>3840</number>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="pageStep">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>1920</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelWidthValue">
|
||||||
|
<property name="minimumWidth">
|
||||||
|
<number>50</number>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>1920</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="labelHeight">
|
||||||
|
<property name="text">
|
||||||
|
<string>Height</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QWidget" name="heightRow" native="true">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayoutHeight">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="sliderHeight">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>480</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>2160</number>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="pageStep">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>1080</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelHeightValue">
|
||||||
|
<property name="minimumWidth">
|
||||||
|
<number>50</number>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>1080</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>sliderWidth</sender>
|
||||||
|
<signal>valueChanged(int)</signal>
|
||||||
|
<receiver>labelWidthValue</receiver>
|
||||||
|
<slot>setNum(int)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>264</x>
|
||||||
|
<y>50</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>475</x>
|
||||||
|
<y>50</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>sliderHeight</sender>
|
||||||
|
<signal>valueChanged(int)</signal>
|
||||||
|
<receiver>labelHeightValue</receiver>
|
||||||
|
<slot>setNum(int)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>264</x>
|
||||||
|
<y>100</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>475</x>
|
||||||
|
<y>100</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>CustomResolutionDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>185</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>179</x>
|
||||||
|
<y>93</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>CustomResolutionDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>421</x>
|
||||||
|
<y>185</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>244</x>
|
||||||
|
<y>93</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include "virtualdisplayrow.h"
|
||||||
|
#include "ui_virtualdisplayrow.h"
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
VirtualDisplayRow::VirtualDisplayRow(QWidget *parent)
|
||||||
|
: QWidget(parent), ui(new Ui::VirtualDisplayRow)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
// Set themed icons at runtime to honor system theme
|
||||||
|
ui->icon->setPixmap(QIcon::fromTheme(QStringLiteral("video-display-symbolic")).pixmap(16, 16));
|
||||||
|
ui->buttonRemove->setIcon(QIcon::fromTheme(QStringLiteral("user-trash-symbolic")));
|
||||||
|
|
||||||
|
connect(ui->buttonRemove, &QPushButton::clicked, this, [this]() {
|
||||||
|
Q_EMIT removeRequested(m_id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDisplayRow::~VirtualDisplayRow() {
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualDisplayRow::setInfo(const QString &id, int w, int h) {
|
||||||
|
m_id = id;
|
||||||
|
ui->labelId->setText(id);
|
||||||
|
ui->labelRes->setText(QStringLiteral("%1x%2").arg(w).arg(h));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace Ui { class VirtualDisplayRow; }
|
||||||
|
|
||||||
|
class VirtualDisplayRow : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit VirtualDisplayRow(QWidget *parent = nullptr);
|
||||||
|
~VirtualDisplayRow() override;
|
||||||
|
|
||||||
|
void setInfo(const QString &id, int w, int h);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void removeRequested(const QString &id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::VirtualDisplayRow *ui;
|
||||||
|
QString m_id;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>VirtualDisplayRow</class>
|
||||||
|
<widget class="QWidget" name="VirtualDisplayRow">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="spacer" native="true">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="icon"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelId">
|
||||||
|
<property name="text">
|
||||||
|
<string>ID</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelRes">
|
||||||
|
<property name="text">
|
||||||
|
<string>WxH</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="buttonRemove">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Remove virtual display</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
Loading…
Reference in New Issue