Commit 039337cd authored by Roman Gilg's avatar Roman Gilg
Browse files

feat(kcm): add option for auto rotation

Summary:
This makes auto rotation configurable from the KScreen KCM. The auto rotation
feature is enabled when a rotation sensor is active and the output is internal,
otherwise the controls are disabled and we fall back to manual configuration.

An open question at this point is if there should be an additional option to
only rotate automatically when tablet mode is active or always. Currently it
always rotates automatically.

{F7820617}

Test Plan: Tested on laptops with and without rotation sensor.

Reviewers: #plasma

Subscribers: apol, aaronhoneycutt, sasch, ngraham, davidedmundson

Maniphest Tasks: T11475

Differential Revision: https://phabricator.kde.org/D26038
parent 80328814
......@@ -368,9 +368,8 @@ bool ControlConfig::getAutoRotate(const QString &outputId, const QString &output
}
// Info for output not found.
// TODO: make this return value depend on the device having a tablet state?
return true;
}
return true;
}
void ControlConfig::setAutoRotate(const KScreen::OutputPtr &output, bool value)
{
......@@ -410,6 +409,74 @@ void ControlConfig::setAutoRotate(const QString &outputId, const QString &output
setOutputAutoRotate();
}
bool ControlConfig::getAutoRotateOnlyInTabletMode(const KScreen::OutputPtr &output) const
{
return getAutoRotateOnlyInTabletMode(output->hashMd5(), output->name());
}
bool ControlConfig::getAutoRotateOnlyInTabletMode(const QString &outputId,
const QString &outputName) const
{
const auto retention = getOutputRetention(outputId, outputName);
if (retention == OutputRetention::Individual) {
const QVariantList outputsInfo = getOutputs();
for (const auto variantInfo : outputsInfo) {
const QVariantMap info = variantInfo.toMap();
if (!infoIsOutput(info, outputId, outputName)) {
continue;
}
const auto val = info[QStringLiteral("autorotate-tablet-only")];
return !val.canConvert<bool>() || val.toBool();
}
}
// Retention is global or info for output not in config control file.
if (auto *outputControl = getOutputControl(outputId, outputName)) {
return outputControl->getAutoRotateOnlyInTabletMode();
}
// Info for output not found.
return true;
}
void ControlConfig::setAutoRotateOnlyInTabletMode(const KScreen::OutputPtr &output, bool value)
{
setAutoRotateOnlyInTabletMode(output->hashMd5(), output->name(), value);
}
// TODO: combine methods (templated functions)
void ControlConfig::setAutoRotateOnlyInTabletMode(const QString &outputId,
const QString &outputName, bool value)
{
QList<QVariant>::iterator it;
QVariantList outputsInfo = getOutputs();
auto setOutputAutoRotateOnlyInTabletMode = [&outputId, &outputName, value, this]() {
if (auto *control = getOutputControl(outputId, outputName)) {
control->setAutoRotateOnlyInTabletMode(value);
}
};
for (it = outputsInfo.begin(); it != outputsInfo.end(); ++it) {
QVariantMap outputInfo = (*it).toMap();
if (!infoIsOutput(outputInfo, outputId, outputName)) {
continue;
}
outputInfo[QStringLiteral("autorotate-tablet-only")] = value;
*it = outputInfo;
setOutputs(outputsInfo);
setOutputAutoRotateOnlyInTabletMode();
return;
}
// no entry yet, create one
auto outputInfo = createOutputInfo(outputId, outputName);
outputInfo[QStringLiteral("autorotate-tablet-only")] = value;
outputsInfo << outputInfo;
setOutputs(outputsInfo);
setOutputAutoRotateOnlyInTabletMode();
}
KScreen::OutputPtr ControlConfig::getReplicationSource(const KScreen::OutputPtr &output) const
{
return getReplicationSource(output->hashMd5(), output->name());
......@@ -560,3 +627,18 @@ void ControlOutput::setAutoRotate(bool value)
}
infoMap[QStringLiteral("autorotate")] = value;
}
bool ControlOutput::getAutoRotateOnlyInTabletMode() const
{
const auto val = constInfo()[QStringLiteral("autorotate-tablet-only")];
return !val.canConvert<bool>() || val.toBool();
}
void ControlOutput::setAutoRotateOnlyInTabletMode(bool value)
{
auto &infoMap = info();
if (infoMap.isEmpty()) {
infoMap = createOutputInfo(m_output->hashMd5(), m_output->name());
}
infoMap[QStringLiteral("autorotate-tablet-only")] = value;
}
......@@ -87,6 +87,12 @@ public:
void setAutoRotate(const KScreen::OutputPtr &output, bool value);
void setAutoRotate(const QString &outputId, const QString &outputName, bool value);
bool getAutoRotateOnlyInTabletMode(const KScreen::OutputPtr &output) const;
bool getAutoRotateOnlyInTabletMode(const QString &outputId, const QString &outputName) const;
void setAutoRotateOnlyInTabletMode(const KScreen::OutputPtr &output, bool value);
void setAutoRotateOnlyInTabletMode(const QString &outputId, const QString &outputName,
bool value);
KScreen::OutputPtr getReplicationSource(const KScreen::OutputPtr &output) const;
KScreen::OutputPtr getReplicationSource(const QString &outputId,
const QString &outputName) const;
......@@ -128,6 +134,9 @@ public:
bool getAutoRotate() const;
void setAutoRotate(bool value);
bool getAutoRotateOnlyInTabletMode() const;
void setAutoRotateOnlyInTabletMode(bool value);
QString dirPath() const override;
QString filePath() const override;
......
......@@ -8,6 +8,7 @@ set(kcm_kscreen_SRCS
${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 @@ ecm_qt_declare_logging_category(kcm_kscreen_SRCS
add_library(kcm_kscreen MODULE ${kcm_kscreen_SRCS})
target_link_libraries(kcm_kscreen
Qt5::Sensors
KF5::ConfigCore
KF5::CoreAddons
KF5::I18n
......
......@@ -36,6 +36,8 @@ void ConfigHandler::setConfig(KScreen::ConfigPtr config)
{
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 @@ void ConfigHandler::setConfig(KScreen::ConfigPtr config)
initOutput(output);
}
m_lastNormalizedScreenSize = screenSize();
// TODO: put this into m_initialControl
m_initialRetention = getRetention();
Q_EMIT retentionChanged();
......@@ -94,7 +98,7 @@ void ConfigHandler::initOutput(const KScreen::OutputPtr &output)
});
}
void ConfigHandler::updateInitialConfig()
void ConfigHandler::updateInitialData()
{
m_initialRetention = getRetention();
connect(new GetConfigOperation(), &GetConfigOperation::finished,
......@@ -106,6 +110,7 @@ void ConfigHandler::updateInitialConfig()
for (auto output : m_config->outputs()) {
resetScale(output);
}
m_initialControl.reset(new ControlConfig(m_initialConfig));
checkNeedsSave();
});
}
......@@ -147,7 +152,10 @@ void ConfigHandler::checkNeedsSave()
|| 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)
|| autoRotateOnlyInTabletMode(output)
!= m_initialControl->getAutoRotateOnlyInTabletMode(output);
}
if (needsSave) {
Q_EMIT needsSaveChecked(true);
......@@ -302,6 +310,26 @@ void ConfigHandler::setReplicationSource(KScreen::OutputPtr &output,
m_control->setReplicationSource(output, source);
}
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);
}
bool ConfigHandler::autoRotateOnlyInTabletMode(const KScreen::OutputPtr &output) const
{
return m_control->getAutoRotateOnlyInTabletMode(output);
}
void ConfigHandler::setAutoRotateOnlyInTabletMode(KScreen::OutputPtr &output, bool value)
{
m_control->setAutoRotateOnlyInTabletMode(output, value);
}
void ConfigHandler::writeControl()
{
if (!m_control) {
......
......@@ -32,7 +32,7 @@ public:
~ConfigHandler() override = default;
void setConfig(KScreen::ConfigPtr config);
void updateInitialConfig();
void updateInitialData();
OutputModel* outputModel() const {
return m_outputs;
......@@ -53,6 +53,11 @@ public:
KScreen::OutputPtr replicationSource(const KScreen::OutputPtr &output) const;
void setReplicationSource(KScreen::OutputPtr &output, const KScreen::OutputPtr &source);
bool autoRotate(const KScreen::OutputPtr &output) const;
void setAutoRotate(KScreen::OutputPtr &output, bool autoRotate);
bool autoRotateOnlyInTabletMode(const KScreen::OutputPtr &output) const;
void setAutoRotateOnlyInTabletMode(KScreen::OutputPtr &output, bool value);
void writeControl();
void checkNeedsSave();
......@@ -79,6 +84,7 @@ private:
OutputModel *m_outputs = nullptr;
std::unique_ptr<ControlConfig> m_control;
std::unique_ptr<ControlConfig> m_initialControl;
Control::OutputRetention m_initialRetention = Control::OutputRetention::
Undefined;
QSize m_lastNormalizedScreenSize;
......
......@@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "output_identifier.h"
#include "output_model.h"
#include "../common/control.h"
#include "../common/orientation_sensor.h"
#include <kscreen/config.h>
#include <kscreen/getconfigoperation.h>
......@@ -70,22 +71,34 @@ KCMKScreen::KCMKScreen(QObject *parent, const QVariantList &args)
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::availableChanged,
this, &KCMKScreen::orientationSensorAvailableChanged);
}
void KCMKScreen::configReady(ConfigOperation *op)
{
qCDebug(KSCREEN_KCM) << "Reading in config now.";
if (op->hasError()) {
m_config.reset();
Q_EMIT backendError();
return;
}
m_config->setConfig(qobject_cast<GetConfigOperation*>(op)->config());
KScreen::ConfigPtr config = qobject_cast<GetConfigOperation*>(op)->config();
const bool autoRotationSupported =
config->supportedFeatures() & (KScreen::Config::Feature::AutoRotation
| KScreen::Config::Feature::TabletMode);
m_orientationSensor->setEnabled(autoRotationSupported);
m_config->setConfig(config);
setBackendReady(true);
Q_EMIT perOutputScalingChanged();
Q_EMIT primaryOutputSupportedChanged();
Q_EMIT outputReplicationSupportedChanged();
Q_EMIT tabletModeAvailableChanged();
Q_EMIT autoRotationSupportedChanged();
}
void KCMKScreen::forceSave()
......@@ -159,7 +172,7 @@ void KCMKScreen::doSave(bool force)
setNeedsSave(false);
return;
}
m_config->updateInitialConfig();
m_config->updateInitialData();
}
);
}
......@@ -239,6 +252,28 @@ bool KCMKScreen::outputReplicationSupported() const
OutputReplication);
}
bool KCMKScreen::autoRotationSupported() const
{
if (!m_config || !m_config->config()) {
return false;
}
return m_config->config()->supportedFeatures() & (KScreen::Config::Feature::AutoRotation
| KScreen::Config::Feature::TabletMode);
}
bool KCMKScreen::orientationSensorAvailable() const
{
return m_orientationSensor->available();
}
bool KCMKScreen::tabletModeAvailable() const
{
if (!m_config || !m_config->config()) {
return false;
}
return m_config->config()->tabletModeAvailable();
}
void KCMKScreen::setScreenNormalized(bool normalized)
{
if (m_screenNormalized == normalized) {
......
......@@ -24,6 +24,7 @@ class ConfigOperation;
}
class ConfigHandler;
class OrientationSensor;
class OutputIdentifier;
class OutputModel;
......@@ -47,6 +48,13 @@ class KCMKScreen : public KQuickAddons::ConfigModule
NOTIFY globalScaleChanged)
Q_PROPERTY(int outputRetention READ outputRetention WRITE setOutputRetention
NOTIFY outputRetentionChanged)
Q_PROPERTY(bool autoRotationSupported READ autoRotationSupported
NOTIFY autoRotationSupportedChanged)
Q_PROPERTY(bool orientationSensorAvailable READ orientationSensorAvailable
NOTIFY orientationSensorAvailableChanged)
Q_PROPERTY(bool tabletModeAvailable READ tabletModeAvailable
NOTIFY tabletModeAvailableChanged)
public:
explicit KCMKScreen (QObject *parent = nullptr,
......@@ -76,6 +84,10 @@ public:
int outputRetention() const;
void setOutputRetention(int retention);
bool autoRotationSupported() const;
bool orientationSensorAvailable() const;
bool tabletModeAvailable() const;
Q_INVOKABLE void forceSave();
void doSave(bool force);
......@@ -90,6 +102,9 @@ Q_SIGNALS:
void outputReplicationSupportedChanged();
void globalScaleChanged();
void outputRetentionChanged();
void autoRotationSupportedChanged();
void orientationSensorAvailableChanged();
void tabletModeAvailableChanged();
void dangerousSave();
void errorOnSave();
void globalScaleWritten();
......@@ -107,6 +122,7 @@ private:
std::unique_ptr<OutputIdentifier> m_outputIdentifier;
std::unique_ptr<ConfigHandler> m_config;
OrientationSensor *m_orientationSensor;
bool m_backendReady = false;
bool m_screenNormalized = true;
double m_globalScale = 1.;
......
......@@ -49,6 +49,8 @@ QVariant OutputModel::data(const QModelIndex &index, int role) const
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:
......@@ -57,6 +59,10 @@ QVariant OutputModel::data(const QModelIndex &index, int role) const
return m_outputs[index.row()].pos;
case NormalizedPositionRole:
return output->geometry().topLeft();
case AutoRotateRole:
return m_config->autoRotate(output);
case AutoRotateOnlyInTabletModeRole:
return m_config->autoRotateOnlyInTabletMode(output);
case RotationRole:
return output->rotation();
case ScaleRole:
......@@ -133,6 +139,16 @@ bool OutputModel::setData(const QModelIndex &index,
return setRefreshRate(index.row(), value.toInt());
}
break;
case AutoRotateRole:
if (value.canConvert<bool>()) {
return setAutoRotate(index.row(), value.value<bool>());
}
break;
case AutoRotateOnlyInTabletModeRole:
if (value.canConvert<bool>()) {
return setAutoRotateOnlyInTabletMode(index.row(), value.value<bool>());
}
break;
case RotationRole:
if (value.canConvert<KScreen::Output::Rotation>()) {
return setRotation(index.row(),
......@@ -162,10 +178,13 @@ bool OutputModel::setData(const QModelIndex &index,
QHash<int, QByteArray> OutputModel::roleNames() const {
QHash<int, QByteArray> 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[AutoRotateOnlyInTabletModeRole] = "autoRotateOnlyInTabletMode";
roles[RotationRole] = "rotation";
roles[ScaleRole] = "scale";
roles[ResolutionIndexRole] = "resolutionIndex";
......@@ -366,6 +385,34 @@ bool OutputModel::setRefreshRate(int outputIndex, int refIndex)
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::setAutoRotateOnlyInTabletMode(int outputIndex, bool value)
{
Output &output = m_outputs[outputIndex];
if (m_config->autoRotateOnlyInTabletMode(output.ptr) == value) {
return false;
}
m_config->setAutoRotateOnlyInTabletMode(output.ptr, value);
QModelIndex index = createIndex(outputIndex, 0);
Q_EMIT dataChanged(index, index, {AutoRotateOnlyInTabletModeRole});
return true;
}
bool OutputModel::setRotation(int outputIndex, KScreen::Output::Rotation rotation)
{
const Output &output = m_outputs[outputIndex];
......
......@@ -30,12 +30,15 @@ class OutputModel : public QAbstractListModel
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,
AutoRotateOnlyInTabletModeRole,
RotationRole,
ScaleRole,
ResolutionIndexRole,
......@@ -113,6 +116,8 @@ private:
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);
bool setAutoRotateOnlyInTabletMode(int outputIndex, bool value);
int resolutionIndex(const KScreen::OutputPtr &output) const;
int refreshRateIndex(const KScreen::OutputPtr &output) const;
......
......@@ -19,24 +19,64 @@ import QtQuick.Layouts 1.1
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
// TODO: Make this dependend on tablet mode being available
enabled: kcm.orientationSensorAvailable && element.internal
visible: kcm.autoRotationSupported
ColumnLayout {
Controls.RadioButton {
id: autoRotateRadio
text: i18n("Automatic")
checked: autoRotateColumn.enabled && element.autoRotate
onClicked: element.autoRotate = true
}
Controls.CheckBox {
id: autoRotateOnlyInTabletMode
Layout.leftMargin: Kirigami.Units.largeSpacing
text: i18n("Only when in tablet mode.")
enabled: autoRotateRadio.checked
checked: enabled && element.autoRotateOnlyInTabletMode
onClicked: element.autoRotateOnlyInTabletMode = checked
}
}
Controls.RadioButton {
id: manualRotateRadio
text: i18n("Manual")
checked: !element.autoRotate || !autoRotateColumn.enabled
onClicked: element.autoRotate = false
}
}
RowLayout {
id: orientation
enabled: !element.autoRotate || !autoRotateColumn.enabled || !autoRotateColumn.visible
Controls.ButtonGroup {
buttons: orientation.children
}
RotationButton {
value: 0
}
RotationButton {
value: 90
}
RotationButton {
value: 180
}
RotationButton {
value: 270
}
}
}
......@@ -83,7 +83,11 @@ void Config::setDeviceOrientation(QOrientationReading::Orientation orientation)
if (!m_control->getAutoRotate(output)) {
continue;
}
if (Output::updateOrientation(output, orientation)) {
auto finalOrientation = orientation;
if (m_control->getAutoRotateOnlyInTabletMode(output) && !m_data->tabletModeEngaged()) {
finalOrientation = QOrientationReading::Orientation::TopUp;
}
if (Output::updateOrientation(output, finalOrientation)) {
// TODO: call Layouter to find fitting positions for other outputs again
return;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment