diff --git a/kcm/src/CMakeLists.txt b/kcm/src/CMakeLists.txt index c3a918b..9cc944b 100644 --- a/kcm/src/CMakeLists.txt +++ b/kcm/src/CMakeLists.txt @@ -1,52 +1,51 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kcm_displayconfiguration\") include_directories("${CMAKE_CURRENT_SOURCE_DIR}/declarative") set(kcm_kscreen_SRCS declarative/qmloutput.cpp declarative/qmloutputcomponent.cpp declarative/qmlscreen.cpp collapsablebutton.cpp controlpanel.cpp outputconfig.cpp - primaryoutputcombo.cpp unifiedoutputconfig.cpp resolutionslider.cpp utils.cpp widget.cpp previewwidget.cpp scalingconfig.cpp ) ecm_qt_declare_logging_category(kcm_kscreen_SRCS HEADER kcm_screen_debug.h IDENTIFIER KSCREEN_KCM CATEGORY_NAME kscreen.kcm) ki18n_wrap_ui(kcm_kscreen_SRCS stylepreview.ui scaling.ui) add_library(kcm_kscreen MODULE kcm_kscreen.cpp ${kcm_kscreen_SRCS}) target_link_libraries(kcm_kscreen Qt5::QuickWidgets Qt5::Widgets KF5::Screen KF5::I18n KF5::ConfigCore KF5::ConfigWidgets KF5::WidgetsAddons ) install(TARGETS kcm_kscreen DESTINATION ${KDE_INSTALL_PLUGINDIR} ) add_executable(kcm_testapp kcm_testapp.cpp ${kcm_kscreen_SRCS}) set_target_properties(kcm_testapp PROPERTIES COMPILE_FLAGS "-DQT_DECLARATIVE_DEBUG") target_link_libraries(kcm_testapp Qt5::QuickWidgets Qt5::Widgets KF5::CoreAddons KF5::I18n KF5::ConfigCore KF5::Screen KF5::WidgetsAddons ) diff --git a/kcm/src/primaryoutputcombo.cpp b/kcm/src/primaryoutputcombo.cpp deleted file mode 100644 index d9a6520..0000000 --- a/kcm/src/primaryoutputcombo.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2015 Daniel Vrátil - * - * 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) version 3 or any later version - * accepted by the membership of KDE e.V. (or its successor approved - * by the membership of KDE e.V.), which shall act as a proxy - * defined in Section 14 of version 3 of the license. - * - * 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, see . - * - */ - -#include "primaryoutputcombo.h" -#include "utils.h" - -#include - -#include -#include - - -PrimaryOutputCombo::PrimaryOutputCombo(QWidget* parent) - : QComboBox(parent) -{ - connect(this, static_cast(&QComboBox::currentIndexChanged), - this, &PrimaryOutputCombo::onCurrentIndexChanged); - - setSizeAdjustPolicy(QComboBox::AdjustToContents); - addItem(i18n("No Primary Output")); -} - -PrimaryOutputCombo::~PrimaryOutputCombo() -{ -} - -void PrimaryOutputCombo::setConfig(const KScreen::ConfigPtr &config) -{ - if (mConfig) { - mConfig->disconnect(this); - for (const KScreen::OutputPtr &output : mConfig->outputs()) { - output->disconnect(this); - } - } - - clear(); - addItem(i18n("No Primary Output")); - - if (!config) { - return; - } - - mConfig = config; - connect(mConfig.data(), &KScreen::Config::outputAdded, - this, &PrimaryOutputCombo::addOutput); - connect(mConfig.data(), &KScreen::Config::outputRemoved, - this, &PrimaryOutputCombo::removeOutput); - connect(mConfig.data(), &KScreen::Config::primaryOutputChanged, - this, &PrimaryOutputCombo::setPrimaryOutput); - - // Don't emit currentIndexChanged - const bool blocked = blockSignals(true); - for (const KScreen::OutputPtr &output : config->outputs()) { - addOutput(output); - } - blockSignals(blocked); -} - -void PrimaryOutputCombo::addOutput(const KScreen::OutputPtr &output) -{ - connect(output.data(), &KScreen::Output::isConnectedChanged, - [output, this]() { - outputChanged(output); - }); - connect(output.data(), &KScreen::Output::isEnabledChanged, - [output, this]() { - outputChanged(output); - }); - - if (!output->isConnected() || !output->isEnabled()) { - return; - } - - addOutputItem(output); -} - -void PrimaryOutputCombo::removeOutput(int outputId) -{ - KScreen::OutputPtr output = mConfig->output(outputId); - if (output) { - output->disconnect(this); - } - - removeOutputItem(outputId); -} - -KScreen::OutputPtr PrimaryOutputCombo::primaryOutput() const -{ - if (!mConfig) { - return KScreen::OutputPtr(); - } - - const int index = currentIndex(); - // "No Primary Output" item - if (index == 0) { - return KScreen::OutputPtr(); - } - - return mConfig->output(itemData(index).toInt()); -} - -void PrimaryOutputCombo::setPrimaryOutput(const KScreen::OutputPtr &output) -{ - Q_ASSERT(mConfig); - - const int index = output ? findData(output->id()) : 0; - if (index == -1) { - return; - } - - if (index == currentIndex()) { - return; - } - - setCurrentIndex(index); -} - - -void PrimaryOutputCombo::outputChanged(const KScreen::OutputPtr &output) -{ - const int index = findData(output->id()); - if (index == -1 && output->isConnected() && output->isEnabled()) { - addOutputItem(output); - } else if (index > 0 && (!output->isConnected() || !output->isEnabled())) { - removeOutputItem(output->id()); - } -} - -void PrimaryOutputCombo::addOutputItem(const KScreen::OutputPtr& output) -{ - addItem(Utils::outputName(output), output->id()); - if (output->isPrimary()) { - setPrimaryOutput(output); - } -} - -void PrimaryOutputCombo::removeOutputItem(int outputId) -{ - const int index = findData(outputId); - if (index == -1) { - return; - } - - if (index == currentIndex()) { - // We'll get the actual primary update signal eventually - // Don't emit currentIndexChanged - const bool blocked = blockSignals(true); - setCurrentIndex(0); - blockSignals(blocked); - } - removeItem(index); -} - -void PrimaryOutputCombo::onCurrentIndexChanged(int currentIndex) -{ - if (!mConfig) { - return; - } - - const KScreen::OutputPtr newPrimary = currentIndex == 0 ? KScreen::OutputPtr() : mConfig->output(itemData(currentIndex).toInt()); - if (newPrimary == mConfig->primaryOutput()) { - return; - } - - mConfig->setPrimaryOutput(newPrimary); - Q_EMIT changed(); -} - diff --git a/kcm/src/primaryoutputcombo.h b/kcm/src/primaryoutputcombo.h deleted file mode 100644 index 66d5acb..0000000 --- a/kcm/src/primaryoutputcombo.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2015 Daniel Vrátil - * - * 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) version 3 or any later version - * accepted by the membership of KDE e.V. (or its successor approved - * by the membership of KDE e.V.), which shall act as a proxy - * defined in Section 14 of version 3 of the license. - * - * 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, see . - * - */ - -#ifndef PRIMARYOUTPUTCOMBO_H -#define PRIMARYOUTPUTCOMBO_H - -#include - -#include - -class PrimaryOutputCombo : public QComboBox -{ - Q_OBJECT -public: - explicit PrimaryOutputCombo(QWidget *parent = nullptr); - ~PrimaryOutputCombo() override; - - void setConfig(const KScreen::ConfigPtr &config); - KScreen::OutputPtr primaryOutput() const; - -Q_SIGNALS: - void changed(); - -private Q_SLOTS: - void addOutput(const KScreen::OutputPtr &output); - void removeOutput(int outputId); - void setPrimaryOutput(const KScreen::OutputPtr &output); - void outputChanged(const KScreen::OutputPtr &output); - - void onCurrentIndexChanged(int currentIndex); - -private: - void addOutputItem(const KScreen::OutputPtr &output); - void removeOutputItem(int outputId); - -private: - KScreen::ConfigPtr mConfig; -}; - -#endif diff --git a/kcm/src/widget.cpp b/kcm/src/widget.cpp index 22240f2..4d7fcb1 100644 --- a/kcm/src/widget.cpp +++ b/kcm/src/widget.cpp @@ -1,490 +1,591 @@ /* * Copyright (C) 2013 Daniel Vr??til * * 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 "widget.h" #include "controlpanel.h" #ifdef WITH_PROFILES #include "profilesmodel.h" #endif -#include "primaryoutputcombo.h" #include #include #include #include #include #include "declarative/qmloutput.h" #include "declarative/qmlscreen.h" #include "utils.h" #include "scalingconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define QML_PATH "kcm_kscreen/qml/" Widget::Widget(QWidget *parent) : QWidget(parent) { qRegisterMetaType(); setMinimumHeight(550); QVBoxLayout *layout = new QVBoxLayout(this); QSplitter *splitter = new QSplitter(Qt::Vertical, this); layout->addWidget(splitter); mDeclarativeView = new QQuickWidget(); mDeclarativeView->setResizeMode(QQuickWidget::SizeRootObjectToView); mDeclarativeView->setMinimumHeight(280); splitter->addWidget(mDeclarativeView); QWidget *widget = new QWidget(this); splitter->addWidget(widget); splitter->setStretchFactor(1, 1); widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); QVBoxLayout *vbox = new QVBoxLayout(widget); const int topMargin = style()->pixelMetric(QStyle::PM_LayoutTopMargin, nullptr, this); vbox->setContentsMargins(0, topMargin, 0, 0); widget->setLayout(vbox); QHBoxLayout *hbox = new QHBoxLayout; vbox->addLayout(hbox); - mPrimaryCombo = new PrimaryOutputCombo(this); - connect(mPrimaryCombo, &PrimaryOutputCombo::changed, - this, &Widget::changed); + mPrimaryCombo = new QComboBox(this); + mPrimaryCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); + mPrimaryCombo->addItem(i18n("No Primary Output")); + connect(mPrimaryCombo, QOverload::of(&QComboBox::currentIndexChanged), + this, &Widget::primaryOutputSelected); mPrimaryLabel = new QLabel(i18n("Primary display:")); hbox->addWidget(mPrimaryLabel); hbox->addWidget(mPrimaryCombo); hbox->addStretch(); #ifdef WITH_PROFILES mProfilesModel = new ProfilesModel(this); connect(mProfilesModel, &ProfilesModel::modelUpdated()), this, &Widget::slotProfilesUpdated); mProfilesCombo = new QComboBox(this); mProfilesCombo->setModel(mProfilesModel); mProfilesCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); hbox->addWidget(new QLabel(i18n("Active profile"))); hbox->addWidget(mProfilesCombo); #endif mControlPanel = new ControlPanel(this); connect(mControlPanel, &ControlPanel::changed, this, &Widget::changed); vbox->addWidget(mControlPanel); mUnifyButton = new QPushButton(i18n("Unify Outputs"), this); connect(mUnifyButton, &QPushButton::released, [this]{ slotUnifyOutputs(); }); vbox->addWidget(mUnifyButton); mScaleAllOutputsButton = new QPushButton(i18n("Scale Display"), this); connect(mScaleAllOutputsButton, &QPushButton::released, [this] { QPointer dialog = new ScalingConfig(mConfig->outputs(), this); dialog->exec(); delete dialog; }); vbox->addWidget(mScaleAllOutputsButton); mOutputTimer = new QTimer(this); connect(mOutputTimer, &QTimer::timeout, this, &Widget::clearOutputIdentifiers); loadQml(); } Widget::~Widget() { clearOutputIdentifiers(); } bool Widget::eventFilter(QObject* object, QEvent* event) { if (event->type() == QEvent::Resize) { if (mOutputIdentifiers.contains(qobject_cast(object))) { QResizeEvent *e = static_cast(event); const QRect screenSize = object->property("screenSize").toRect(); QRect geometry(QPoint(0, 0), e->size()); geometry.moveCenter(screenSize.center()); static_cast(object)->setGeometry(geometry); // Pass the event further } } return QObject::eventFilter(object, event); } void Widget::setConfig(const KScreen::ConfigPtr &config) { if (mConfig) { KScreen::ConfigMonitor::instance()->removeConfig(mConfig); for (const KScreen::OutputPtr &output : mConfig->outputs()) { output->disconnect(this); } + mConfig->disconnect(this); } mConfig = config; KScreen::ConfigMonitor::instance()->addConfig(mConfig); + resetPrimaryCombo(); + connect(mConfig.data(), &KScreen::Config::outputAdded, + this, &Widget::outputAdded); + connect(mConfig.data(), &KScreen::Config::outputRemoved, + this, &Widget::outputRemoved); + connect(mConfig.data(), &KScreen::Config::primaryOutputChanged, + this, &Widget::primaryOutputChanged); mScreen->setConfig(mConfig); mControlPanel->setConfig(mConfig); - mPrimaryCombo->setConfig(mConfig); mUnifyButton->setEnabled(mConfig->outputs().count() > 1); mScaleAllOutputsButton->setVisible(!mConfig->supportedFeatures().testFlag(KScreen::Config::Feature::PerOutputScaling)); - mPrimaryCombo->setVisible(mConfig->supportedFeatures().testFlag(KScreen::Config::Feature::PrimaryDisplay)); - mPrimaryLabel->setVisible(mConfig->supportedFeatures().testFlag(KScreen::Config::Feature::PrimaryDisplay)); for (const KScreen::OutputPtr &output : mConfig->outputs()) { - connect(output.data(), &KScreen::Output::isEnabledChanged, - this, &Widget::slotOutputEnabledChanged); - connect(output.data(), &KScreen::Output::posChanged, - this, &Widget::changed); + outputAdded(output); } // Select the primary (or only) output by default QMLOutput *qmlOutput = mScreen->primaryOutput(); if (qmlOutput) { mScreen->setActiveOutput(qmlOutput); } else { if (!mScreen->outputs().isEmpty()) { mScreen->setActiveOutput(mScreen->outputs().at(0)); } } slotOutputEnabledChanged(); } KScreen::ConfigPtr Widget::currentConfig() const { return mConfig; } void Widget::loadQml() { qmlRegisterType("org.kde.kscreen", 1, 0, "QMLOutput"); qmlRegisterType("org.kde.kscreen", 1, 0, "QMLScreen"); qmlRegisterType("org.kde.kscreen", 1, 0, "KScreenOutput"); qmlRegisterType("org.kde.kscreen", 1, 0, "KScreenEdid"); qmlRegisterType("org.kde.kscreen", 1, 0, "KScreenMode"); //const QString file = QDir::currentPath() + "/main.qml"; const QString file = QStandardPaths::locate(QStandardPaths::QStandardPaths::GenericDataLocation, QStringLiteral("kcm_kscreen/qml/main.qml")); mDeclarativeView->setSource(QUrl::fromLocalFile(file)); QQuickItem* rootObject = mDeclarativeView->rootObject(); mScreen = rootObject->findChild(QStringLiteral("outputView")); if (!mScreen) { return; } mScreen->setEngine(mDeclarativeView->engine()); connect(mScreen, &QMLScreen::focusedOutputChanged, this, &Widget::slotFocusedOutputChanged); connect(rootObject->findChild(QStringLiteral("identifyButton")), SIGNAL(clicked()), this, SLOT(slotIdentifyButtonClicked())); } +void Widget::resetPrimaryCombo() +{ + bool isPrimaryDisplaySupported = mConfig->supportedFeatures().testFlag(KScreen::Config::Feature::PrimaryDisplay); + mPrimaryLabel->setVisible(isPrimaryDisplaySupported); + mPrimaryCombo->setVisible(isPrimaryDisplaySupported); + + // Don't emit currentIndexChanged when resetting + bool blocked = mPrimaryCombo->blockSignals(true); + mPrimaryCombo->clear(); + mPrimaryCombo->addItem(i18n("No Primary Output")); + mPrimaryCombo->blockSignals(blocked); + + if (!mConfig) { + return; + } + + for (auto &output: mConfig->outputs()) { + addOutputToPrimaryCombo(output); + } +} + +void Widget::addOutputToPrimaryCombo(const KScreen::OutputPtr &output) +{ + if (!output->isConnected() || !output->isEnabled()) { + return; + } + mPrimaryCombo->addItem(Utils::outputName(output), output->id()); + if (output->isPrimary()) { + Q_ASSERT(mConfig); + int lastIndex = mPrimaryCombo->count() - 1; + mPrimaryCombo->setCurrentIndex(lastIndex); + } +} + void Widget::slotFocusedOutputChanged(QMLOutput *output) { mControlPanel->activateOutput(output->outputPtr()); } void Widget::slotOutputEnabledChanged() { - int enabledOutputsCnt = 0; + resetPrimaryCombo(); + + int enabledOutputsCount = 0; Q_FOREACH (const KScreen::OutputPtr &output, mConfig->outputs()) { if (output->isEnabled()) { - ++enabledOutputsCnt; + ++enabledOutputsCount; } - - if (enabledOutputsCnt > 1) { + if (enabledOutputsCount > 1) { break; } } + mUnifyButton->setEnabled(enabledOutputsCount > 1); +} - mUnifyButton->setEnabled(enabledOutputsCnt > 1); +void Widget::slotOutputConnectedChanged() +{ + resetPrimaryCombo(); } void Widget::slotUnifyOutputs() { QMLOutput *base = mScreen->primaryOutput(); QList clones; if (!base) { for (QMLOutput *output: mScreen->outputs()) { if (output->output()->isConnected() && output->output()->isEnabled()) { base = output; break; } } if (!base) { // WTF? return; } } if (base->isCloneMode()) { setConfig(mPrevConfig); mPrevConfig.clear(); mPrimaryCombo->setEnabled(true); mUnifyButton->setText(i18n("Unify Outputs")); } else { // Clone the current config, so that we can restore it in case user // breaks the cloning mPrevConfig = mConfig->clone(); for (QMLOutput *output: mScreen->outputs()) { if (!output->output()->isConnected()) { continue; } if (!output->output()->isEnabled()) { output->setVisible(false); continue; } if (!base) { base = output; } output->setOutputX(0); output->setOutputY(0); output->output()->setPos(QPoint(0, 0)); output->output()->setClones(QList()); if (base != output) { clones << output->output()->id(); output->setCloneOf(base); output->setVisible(false); } } base->output()->setClones(clones); base->setIsCloneMode(true); mScreen->updateOutputsPlacement(); mPrimaryCombo->setEnabled(false); mControlPanel->setUnifiedOutput(base->outputPtr()); mUnifyButton->setText(i18n("Break Unified Outputs")); } Q_EMIT changed(); } void Widget::slotProfileChanged(int index) { #ifdef WITH_PROFILES const QVariantMap profile = mProfilesCombo->itemData(index, ProfilesModel::ProfileRole).toMap(); const QVariantList outputs = profile[QLatin1String("outputs")].toList(); // FIXME: Copy-pasted from KDED's Serializer::config() KScreen::Config *config = KScreen::Config::current(); KScreen::OutputList outputList = config->outputs(); for (KScreen::Output: output, outputList) { if (!output->isConnected() && output->isEnabled()) { output->setEnabled(false); } } KScreen::Config *outputsConfig = config->clone(); Q_FOREACH(const QVariant & info, outputs) { KScreen::Output *output = findOutput(outputsConfig, info.toMap()); if (!output) { continue; } delete outputList.take(output->id()); outputList.insert(output->id(), output); } config->setOutputs(outputList); setConfig(config); #else Q_UNUSED(index) #endif } // FIXME: Copy-pasted from KDED's Serializer::findOutput() KScreen::OutputPtr Widget::findOutput(const KScreen::ConfigPtr &config, const QVariantMap &info) { KScreen::OutputList outputs = config->outputs(); Q_FOREACH(const KScreen::OutputPtr &output, outputs) { if (!output->isConnected()) { continue; } const QString outputId = (output->edid() && output->edid()->isValid()) ? output->edid()->hash() : output->name(); if (outputId != info[QStringLiteral("id")].toString()) { continue; } QVariantMap posInfo = info[QStringLiteral("pos")].toMap(); QPoint point(posInfo[QStringLiteral("x")].toInt(), posInfo[QStringLiteral("y")].toInt()); output->setPos(point); output->setPrimary(info[QStringLiteral("primary")].toBool()); output->setEnabled(info[QStringLiteral("enabled")].toBool()); output->setRotation(static_cast(info[QStringLiteral("rotation")].toInt())); QVariantMap modeInfo = info[QStringLiteral("mode")].toMap(); QVariantMap modeSize = modeInfo[QStringLiteral("size")].toMap(); QSize size(modeSize[QStringLiteral("width")].toInt(), modeSize[QStringLiteral("height")].toInt()); const KScreen::ModeList modes = output->modes(); Q_FOREACH(const KScreen::ModePtr &mode, modes) { if (mode->size() != size) { continue; } if (QString::number(mode->refreshRate()) != modeInfo[QStringLiteral("refresh")].toString()) { continue; } output->setCurrentModeId(mode->id()); break; } return output; } return KScreen::OutputPtr(); } void Widget::slotProfilesAboutToUpdate() { #ifdef WITH_PROFILES disconnect(mProfilesCombo, &QComboBox::currentIndexChanged, this, &Widget::slotProfileChanged); #endif } void Widget::slotProfilesUpdated() { #ifdef WITH_PROFILES connect(mProfilesCombo, &QComboBox::currentIndexChanged, this, &Widget::slotProfileChanged); const int index = mProfilesModel->activeProfileIndex(); mProfilesCombo->setCurrentIndex(index); #endif } void Widget::clearOutputIdentifiers() { mOutputTimer->stop(); qDeleteAll(mOutputIdentifiers); mOutputIdentifiers.clear(); } +void Widget::outputAdded(const KScreen::OutputPtr &output) +{ + connect(output.data(), &KScreen::Output::isConnectedChanged, + this, &Widget::slotOutputConnectedChanged); + connect(output.data(), &KScreen::Output::isEnabledChanged, + this, &Widget::slotOutputEnabledChanged); + connect(output.data(), &KScreen::Output::posChanged, + this, &Widget::changed); + + addOutputToPrimaryCombo(output); +} + +void Widget::outputRemoved(int outputId) +{ + KScreen::OutputPtr output = mConfig->output(outputId); + if (!output.isNull()) { + output->disconnect(this); + } + + const int index = mPrimaryCombo->findData(outputId); + if (index == -1) { + return; + } + + if (index == mPrimaryCombo->currentIndex()) { + // We'll get the actual primary update signal eventually + // Don't emit currentIndexChanged + const bool blocked = mPrimaryCombo->blockSignals(true); + mPrimaryCombo->setCurrentIndex(0); + mPrimaryCombo->blockSignals(blocked); + } + mPrimaryCombo->removeItem(index); +} + +void Widget::primaryOutputSelected(int index) +{ + if (!mConfig) { + return; + } + + const KScreen::OutputPtr newPrimary = index == 0 ? KScreen::OutputPtr() : mConfig->output(mPrimaryCombo->itemData(index).toInt()); + if (newPrimary == mConfig->primaryOutput()) { + return; + } + + mConfig->setPrimaryOutput(newPrimary); + Q_EMIT changed(); +} + +void Widget::primaryOutputChanged(const KScreen::OutputPtr &output) +{ + Q_ASSERT(mConfig); + int index = output.isNull() ? 0 : mPrimaryCombo->findData(output->id()); + if (index == -1 || index == mPrimaryCombo->currentIndex()) { + return; + } + mPrimaryCombo->setCurrentIndex(index); +} + void Widget::slotIdentifyButtonClicked(bool checked) { Q_UNUSED(checked); connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, this, &Widget::slotIdentifyOutputs); } void Widget::slotIdentifyOutputs(KScreen::ConfigOperation *op) { if (op->hasError()) { return; } const KScreen::ConfigPtr config = qobject_cast(op)->config(); const QString qmlPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral(QML_PATH "OutputIdentifier.qml")); mOutputTimer->stop(); clearOutputIdentifiers(); /* Obtain the current active configuration from KScreen */ Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { if (!output->isConnected() || !output->currentMode()) { continue; } const KScreen::ModePtr mode = output->currentMode(); QQuickView *view = new QQuickView(); view->setFlags(Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint); view->setResizeMode(QQuickView::SizeViewToRootObject); view->setSource(QUrl::fromLocalFile(qmlPath)); view->installEventFilter(this); QQuickItem *rootObj = view->rootObject(); if (!rootObj) { qWarning() << "Failed to obtain root item"; continue; } QSize deviceSize, logicalSize; if (output->isHorizontal()) { deviceSize = mode->size(); } else { deviceSize = QSize(mode->size().height(), mode->size().width()); } if (config->supportedFeatures() & KScreen::Config::Feature::PerOutputScaling) { // no scale adjustment needed on Wayland logicalSize = deviceSize; } else { logicalSize = deviceSize / devicePixelRatioF(); } rootObj->setProperty("outputName", Utils::outputName(output)); rootObj->setProperty("modeName", Utils::sizeToString(deviceSize)); view->setProperty("screenSize", QRect(output->pos(), logicalSize)); mOutputIdentifiers << view; } for (QQuickView *view: mOutputIdentifiers) { view->show(); } mOutputTimer->start(2500); } diff --git a/kcm/src/widget.h b/kcm/src/widget.h index 209ccac..c5b94ae 100644 --- a/kcm/src/widget.h +++ b/kcm/src/widget.h @@ -1,106 +1,113 @@ /* * Copyright (C) 2013 Daniel Vrátil * * 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. * */ #ifndef WIDGET_H #define WIDGET_H #include #include #include class ProfilesModel; class QLabel; class QMLOutput; class QMLScreen; class ControlPanel; class PrimaryOutputCombo; class QPushButton; class QComboBox; class QQuickView; class QQuickWidget; namespace KScreen { class ConfigOperation; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = nullptr); ~Widget() override; void setConfig(const KScreen::ConfigPtr &config); KScreen::ConfigPtr currentConfig() const; protected: bool eventFilter(QObject *object, QEvent *event) override; Q_SIGNALS: void changed(); private Q_SLOTS: void slotFocusedOutputChanged(QMLOutput *output); void slotOutputEnabledChanged(); + void slotOutputConnectedChanged(); void slotUnifyOutputs(); void slotProfileChanged(int index); void slotProfilesAboutToUpdate(); void slotProfilesUpdated(); void slotIdentifyButtonClicked(bool checked = true); void slotIdentifyOutputs(KScreen::ConfigOperation *op); void clearOutputIdentifiers(); + void outputAdded(const KScreen::OutputPtr &output); + void outputRemoved(int outputId); + void primaryOutputSelected(int index); + void primaryOutputChanged(const KScreen::OutputPtr &output); + private: void loadQml(); - void initPrimaryCombo(); + void resetPrimaryCombo(); + void addOutputToPrimaryCombo(const KScreen::OutputPtr &output); KScreen::OutputPtr findOutput(const KScreen::ConfigPtr &config, const QVariantMap &info); private: QMLScreen *mScreen = nullptr; KScreen::ConfigPtr mConfig = nullptr; KScreen::ConfigPtr mPrevConfig = nullptr; QQuickWidget *mDeclarativeView = nullptr; ControlPanel *mControlPanel = nullptr; ProfilesModel *mProfilesModel = nullptr; - PrimaryOutputCombo *mPrimaryCombo = nullptr; + QComboBox *mPrimaryCombo = nullptr; QLabel *mPrimaryLabel = nullptr; QComboBox *mProfilesCombo = nullptr; QPushButton *mScaleAllOutputsButton = nullptr; QPushButton *mUnifyButton = nullptr; QPushButton *mSaveProfileButton = nullptr; QList mOutputIdentifiers; QTimer *mOutputTimer = nullptr; }; #endif // WIDGET_H