Index: kcm/CMakeLists.txt =================================================================== --- kcm/CMakeLists.txt +++ kcm/CMakeLists.txt @@ -8,6 +8,7 @@ ${CMAKE_SOURCE_DIR}/common/utils.cpp ${CMAKE_SOURCE_DIR}/common/control.cpp ${CMAKE_SOURCE_DIR}/common/globals.cpp + ${CMAKE_SOURCE_DIR}/common/orientation_sensor.cpp ) ecm_qt_declare_logging_category(kcm_kscreen_SRCS @@ -22,6 +23,7 @@ add_library(kcm_kscreen MODULE ${kcm_kscreen_SRCS}) target_link_libraries(kcm_kscreen + Qt5::Sensors KF5::ConfigCore KF5::CoreAddons KF5::I18n Index: kcm/config_handler.h =================================================================== --- kcm/config_handler.h +++ kcm/config_handler.h @@ -32,7 +32,7 @@ ~ConfigHandler() override = default; void setConfig(KScreen::ConfigPtr config); - void updateInitialConfig(); + void updateInitialData(); OutputModel* outputModel() const { return m_outputs; @@ -47,6 +47,10 @@ int retention() const; void setRetention(int retention); + + bool autoRotate(const KScreen::OutputPtr &output) const; + void setAutoRotate(KScreen::OutputPtr &output, bool autoRotate); + void writeControl(); void checkNeedsSave(); @@ -72,6 +76,7 @@ OutputModel *m_outputs = nullptr; std::unique_ptr m_control; + std::unique_ptr m_initialControl; Control::OutputRetention m_initialRetention = Control::OutputRetention:: Undefined; QSize m_lastNormalizedScreenSize; Index: kcm/config_handler.cpp =================================================================== --- kcm/config_handler.cpp +++ kcm/config_handler.cpp @@ -36,6 +36,8 @@ { m_config = config; m_initialConfig = m_config->clone(); + m_initialControl.reset(new ControlConfig(m_initialConfig)); + KScreen::ConfigMonitor::instance()->addConfig(m_config); m_control.reset(new ControlConfig(config)); @@ -49,6 +51,8 @@ initOutput(output); } m_lastNormalizedScreenSize = screenSize(); + + // TODO: put this into m_initialControl m_initialRetention = getRetention(); Q_EMIT retentionChanged(); @@ -78,15 +82,16 @@ }); } -void ConfigHandler::updateInitialConfig() +void ConfigHandler::updateInitialData() { m_initialRetention = getRetention(); connect(new GetConfigOperation(), &GetConfigOperation::finished, this, [this](ConfigOperation *op) { if (op->hasError()) { return; } m_initialConfig = qobject_cast(op)->config(); + m_initialControl.reset(new ControlConfig(m_initialConfig)); checkNeedsSave(); }); } @@ -128,7 +133,8 @@ || output->pos() != initialOutput->pos() || output->scale() != initialOutput->scale() || output->rotation() != initialOutput->rotation() - || output->replicationSource() != initialOutput->replicationSource(); + || output->replicationSource() != initialOutput->replicationSource() + || autoRotate(output) != m_initialControl->getAutoRotate(output); } if (needsSave) { Q_EMIT needsSaveChecked(true); @@ -262,6 +268,16 @@ Q_EMIT changed(); } +bool ConfigHandler::autoRotate(const KScreen::OutputPtr &output) const +{ + return m_control->getAutoRotate(output); +} + +void ConfigHandler::setAutoRotate(KScreen::OutputPtr &output, bool autoRotate) +{ + m_control->setAutoRotate(output, autoRotate); +} + void ConfigHandler::writeControl() { if (!m_control) { Index: kcm/kcm.h =================================================================== --- kcm/kcm.h +++ kcm/kcm.h @@ -24,6 +24,7 @@ } class ConfigHandler; +class OrientationSensor; class OutputIdentifier; class OutputModel; @@ -47,6 +48,9 @@ NOTIFY globalScaleChanged) Q_PROPERTY(int outputRetention READ outputRetention WRITE setOutputRetention NOTIFY outputRetentionChanged) + Q_PROPERTY(bool orientationSensorAvailable READ orientationSensorAvailable + NOTIFY orientationSensorAvailableChanged) + public: explicit KCMKScreen (QObject *parent = nullptr, @@ -76,6 +80,8 @@ int outputRetention() const; void setOutputRetention(int retention); + bool orientationSensorAvailable() const; + Q_INVOKABLE void forceSave(); void doSave(bool force); @@ -90,6 +96,7 @@ void outputReplicationSupportedChanged(); void globalScaleChanged(); void outputRetentionChanged(); + void orientationSensorAvailableChanged(); void dangerousSave(); void errorOnSave(); void globalScaleWritten(); @@ -107,6 +114,7 @@ std::unique_ptr m_outputIdentifier; std::unique_ptr m_config; + OrientationSensor *m_orientationSensor; bool m_backendReady = false; bool m_screenNormalized = true; double m_globalScale = 1.; Index: kcm/kcm.cpp =================================================================== --- kcm/kcm.cpp +++ kcm/kcm.cpp @@ -21,6 +21,7 @@ #include "output_identifier.h" #include "output_model.h" #include "../common/control.h" +#include "../common/orientation_sensor.h" #include #include @@ -70,6 +71,10 @@ m_loadCompressor->setInterval(1000); m_loadCompressor->setSingleShot(true); connect (m_loadCompressor, &QTimer::timeout, this, &KCMKScreen::load); + + m_orientationSensor = new OrientationSensor(this); + connect(m_orientationSensor, &OrientationSensor::validChanged, + this, &KCMKScreen::orientationSensorAvailableChanged); } void KCMKScreen::configReady(ConfigOperation *op) @@ -159,7 +164,7 @@ setNeedsSave(false); return; } - m_config->updateInitialConfig(); + m_config->updateInitialData(); } ); } @@ -239,6 +244,11 @@ OutputReplication); } +bool KCMKScreen::orientationSensorAvailable() const +{ + return m_orientationSensor->isValid(); +} + void KCMKScreen::setScreenNormalized(bool normalized) { if (m_screenNormalized == normalized) { Index: kcm/output_model.h =================================================================== --- kcm/output_model.h +++ kcm/output_model.h @@ -30,12 +30,14 @@ public: enum OutputRoles { EnabledRole = Qt::UserRole + 1, + InternalRole, PrimaryRole, SizeRole, /** Position in the graphical view relative to some arbitrary but fixed origin. */ PositionRole, /** Position for backend relative to most northwest display corner. */ NormalizedPositionRole, + AutoRotateRole, RotationRole, ScaleRole, ResolutionIndexRole, @@ -113,6 +115,7 @@ bool setResolution(int outputIndex, int resIndex); bool setRefreshRate(int outputIndex, int refIndex); bool setRotation(int outputIndex, KScreen::Output::Rotation rotation); + bool setAutoRotate(int outputIndex, bool value); int resolutionIndex(const KScreen::OutputPtr &output) const; int refreshRateIndex(const KScreen::OutputPtr &output) const; Index: kcm/output_model.cpp =================================================================== --- kcm/output_model.cpp +++ kcm/output_model.cpp @@ -49,14 +49,18 @@ return Utils::outputName(output); case EnabledRole: return output->isEnabled(); + case InternalRole: + return output->type() == KScreen::Output::Type::Panel; case PrimaryRole: return output->isPrimary(); case SizeRole: return output->geometry().size(); case PositionRole: return m_outputs[index.row()].pos; case NormalizedPositionRole: return output->geometry().topLeft(); + case AutoRotateRole: + return m_config->autoRotate(output); case RotationRole: return output->rotation(); case ScaleRole: @@ -133,6 +137,11 @@ return setRefreshRate(index.row(), value.toInt()); } break; + case AutoRotateRole: + if (value.canConvert()) { + return setAutoRotate(index.row(), value.value()); + } + break; case RotationRole: if (value.canConvert()) { return setRotation(index.row(), @@ -161,10 +170,12 @@ QHash OutputModel::roleNames() const { QHash roles = QAbstractItemModel::roleNames(); roles[EnabledRole] = "enabled"; + roles[InternalRole] = "internal"; roles[PrimaryRole] = "primary"; roles[SizeRole] = "size"; roles[PositionRole] = "position"; roles[NormalizedPositionRole] = "normalizedPosition"; + roles[AutoRotateRole] = "autoRotate"; roles[RotationRole] = "rotation"; roles[ScaleRole] = "scale"; roles[ResolutionIndexRole] = "resolutionIndex"; @@ -365,6 +376,20 @@ return true; } +bool OutputModel::setAutoRotate(int outputIndex, bool value) +{ + Output &output = m_outputs[outputIndex]; + + if (m_config->autoRotate(output.ptr) == value) { + return false; + } + m_config->setAutoRotate(output.ptr, value); + + QModelIndex index = createIndex(outputIndex, 0); + Q_EMIT dataChanged(index, index, {AutoRotateRole}); + return true; +} + bool OutputModel::setRotation(int outputIndex, KScreen::Output::Rotation rotation) { const Output &output = m_outputs[outputIndex]; Index: kcm/package/contents/ui/Orientation.qml =================================================================== --- kcm/package/contents/ui/Orientation.qml +++ kcm/package/contents/ui/Orientation.qml @@ -19,24 +19,51 @@ import QtQuick.Controls 2.3 as Controls import org.kde.kirigami 2.4 as Kirigami -RowLayout { - id: orientation - Kirigami.FormData.label: i18n("Orientation:") - - Controls.ButtonGroup { - buttons: orientation.children - } - - RotationButton { - value: 0 - } - RotationButton { - value: 90 - } - RotationButton { - value: 180 - } - RotationButton { - value: 270 - } +ColumnLayout { + Kirigami.FormData.label: i18n("Orientation:") + Kirigami.FormData.buddyFor: autoRotateRadio + spacing: Kirigami.Units.smallSpacing + + ColumnLayout { + id: autoRotateColumn + enabled: kcm.orientationSensorAvailable && element.internal + // TODO: Make this dependend on: + // * tablet mode being available (?) + + Controls.RadioButton { + id: autoRotateRadio + text: i18n("Automatic") + checked: autoRotateColumn.enabled && element.autoRotate + onClicked: element.autoRotate = true + } + + Controls.RadioButton { + id: manualRotateRadio + text: i18n("Manual") + checked: !element.autoRotate || !autoRotateColumn.enabled + onClicked: element.autoRotate = false + } + } + + RowLayout { + id: orientation + enabled: !element.autoRotate || !autoRotateColumn.enabled + + Controls.ButtonGroup { + buttons: orientation.children + } + + RotationButton { + value: 0 + } + RotationButton { + value: 90 + } + RotationButton { + value: 180 + } + RotationButton { + value: 270 + } + } }