Add support for custom resolutions

Add build-time check for qt6-quick3d
This commit is contained in:
wheaney 2025-09-20 17:44:00 -07:00
parent 645a38176e
commit 5ec52baa3f
12 changed files with 631 additions and 76 deletions

View File

@ -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(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS KCMUtils)
find_package(KWin REQUIRED COMPONENTS kwineffects) find_package(KWin REQUIRED COMPONENTS kwineffects)
message(STATUS "Found KWin Version: ${KWin_VERSION}") 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)

View File

@ -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

View File

@ -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 ()

View File

@ -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})

View File

@ -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);
} }
} }

View File

@ -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>

View File

@ -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();
}

View File

@ -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;
};

View File

@ -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>

View File

@ -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));
}

View File

@ -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;
};

View File

@ -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>