mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 14:23:09 +00:00
GestureNavigation: Screen Rotation Popup Button
Implementation of a popup button to rotate the screen while using gesture navigation. The button is set to appear when the device rotates while auto rotation is off. Then the button will be visible for a short period of time before disappearing.
This commit is contained in:
parent
842354bd70
commit
9bd4f0b747
12 changed files with 518 additions and 139 deletions
|
|
@ -7,6 +7,7 @@ add_subdirectory(mmplugin)
|
|||
add_subdirectory(mobileshell)
|
||||
add_subdirectory(mobileshellstate)
|
||||
add_subdirectory(quicksettingsplugin)
|
||||
add_subdirectory(rotationplugin)
|
||||
add_subdirectory(windowplugin)
|
||||
add_subdirectory(shellsettingsplugin)
|
||||
add_subdirectory(wallpaperimageplugin)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ Item {
|
|||
function load() {
|
||||
volumeOSD.active = true;
|
||||
notifications.active = true;
|
||||
actionButtons.active = true;
|
||||
}
|
||||
|
||||
Loader {
|
||||
|
|
@ -37,4 +38,11 @@ Item {
|
|||
MobileShell.NotificationPopupProvider {}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: actionButtons
|
||||
sourceComponent: Component {
|
||||
MobileShell.ActionButtonsProvider {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
186
components/mobileshell/qml/popups/actionbuttons/ActionButton.qml
Normal file
186
components/mobileshell/qml/popups/actionbuttons/ActionButton.qml
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Micah Stanley <stanleymicah@proton.me>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
import org.kde.plasma.private.mobileshell.state as MobileShellState
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
|
||||
import org.kde.layershell 1.0 as LayerShell
|
||||
|
||||
|
||||
Window {
|
||||
id: root
|
||||
|
||||
readonly property int size: Kirigami.Units.gridUnit * 2
|
||||
readonly property int margins: Math.round(Kirigami.Units.largeSpace * 0.5)
|
||||
|
||||
property int screenCorner: ActionButton.ScreenCorner.BottomRight
|
||||
property int angle: 0
|
||||
property string iconSource
|
||||
property bool active: false
|
||||
|
||||
signal triggered()
|
||||
|
||||
enum ScreenCorner {
|
||||
BottomRight,
|
||||
BottomLeft,
|
||||
TopLeft,
|
||||
TopRight
|
||||
}
|
||||
|
||||
// When the button is animating its disappearance, make sure it is transparent to inputs.
|
||||
onActiveChanged: {
|
||||
ShellUtil.setInputTransparent(root, !active)
|
||||
if (active) {
|
||||
root.visible = true;
|
||||
root.raise();
|
||||
hideButton.stop();
|
||||
return;
|
||||
}
|
||||
hideButton.restart();
|
||||
}
|
||||
|
||||
LayerShell.Window.scope: "overlay"
|
||||
LayerShell.Window.margins.top: margins
|
||||
LayerShell.Window.margins.bottom: margins
|
||||
LayerShell.Window.margins.left: margins
|
||||
LayerShell.Window.margins.right: margins
|
||||
LayerShell.Window.layer: LayerShell.Window.LayerOverlay
|
||||
LayerShell.Window.exclusionZone: -1
|
||||
LayerShell.Window.keyboardInteractivity: LayerShell.Window.KeyboardInteractivityNone
|
||||
LayerShell.Window.anchors: {
|
||||
if (screenCorner === ActionButton.ScreenCorner.TopLeft) {
|
||||
return LayerShell.Window.AnchorTop | LayerShell.Window.AnchorLeft
|
||||
} else if (screenCorner === ActionButton.ScreenCorner.BottomRight) {
|
||||
return LayerShell.Window.AnchorBottom | LayerShell.Window.AnchorRight
|
||||
} else if (screenCorner === ActionButton.ScreenCorner.BottomLeft) {
|
||||
return LayerShell.Window.AnchorBottom | LayerShell.Window.AnchorLeft
|
||||
} else {
|
||||
return LayerShell.Window.AnchorTop | LayerShell.Window.AnchorRight
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
// Double the set button size to leave room for button scale animation.
|
||||
width: size * 2
|
||||
height: size * 2
|
||||
|
||||
visible: active
|
||||
|
||||
color: "transparent"
|
||||
|
||||
// Hide the root window after the button disappearing animation finishes.
|
||||
Timer {
|
||||
id: hideButton
|
||||
interval: Kirigami.Units.longDuration
|
||||
repeat: false
|
||||
onTriggered: if (!active) root.visible = false;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// Because the window surface area had to be made larger to accommodate the button scale animation,
|
||||
// set the input region to the size of the actual button.
|
||||
ShellUtil.setInputRegion(root, Qt.rect((root.width - size) / 2, (root.height - size) / 2, size, size));
|
||||
ShellUtil.setInputTransparent(root, !active);
|
||||
}
|
||||
|
||||
Controls.Control {
|
||||
id: content
|
||||
anchors.centerIn: parent
|
||||
width: root.size
|
||||
height: root.size
|
||||
opacity: root.active ? 1 : 0
|
||||
|
||||
property double scale: !root.active ? 0.5 : (button.pressed ? 1.5 : 1)
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.OutBack
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.OutCirc
|
||||
}
|
||||
}
|
||||
|
||||
transform: Scale {
|
||||
origin.x: root.size / 2
|
||||
origin.y: root.size / 2
|
||||
xScale: content.scale
|
||||
yScale: content.scale
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
source: simpleShadow
|
||||
blurMax: 16
|
||||
shadowEnabled: true
|
||||
shadowVerticalOffset: 1
|
||||
shadowOpacity: 0.85
|
||||
shadowColor: Qt.lighter(Kirigami.Theme.backgroundColor, 0.2)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: simpleShadow
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: -1
|
||||
anchors.rightMargin: -1
|
||||
anchors.bottomMargin: -1
|
||||
|
||||
color: {
|
||||
let darkerBackgroundColor = Qt.darker(Kirigami.Theme.backgroundColor, 1.3);
|
||||
return Qt.rgba(darkerBackgroundColor.r, darkerBackgroundColor.g, darkerBackgroundColor.b, 0.5)
|
||||
}
|
||||
radius: root.size
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.lighter(Kirigami.Theme.backgroundColor, 1.5)
|
||||
radius: root.size
|
||||
opacity: 0.85
|
||||
}
|
||||
|
||||
Controls.AbstractButton {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
|
||||
MobileShell.HapticsEffect {
|
||||
id: haptics
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
Kirigami.Icon {
|
||||
anchors.centerIn: parent
|
||||
width: Kirigami.Units.iconSizes.small
|
||||
height: Kirigami.Units.iconSizes.small
|
||||
transformOrigin: Item.Center
|
||||
rotation: root.angle
|
||||
source: root.iconSource
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
haptics.buttonVibrate();
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
if (active) root.triggered();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Micah Stanley <stanleymicah@proton.me>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.private.mobileshell.state as MobileShellState
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
|
||||
/**
|
||||
* This sets up the popup action buttons.
|
||||
*/
|
||||
QtObject {
|
||||
id: component
|
||||
|
||||
property var rotationButton: RotationButton {}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Micah Stanley <stanleymicah@proton.me>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.private.mobileshell.rotationplugin as RotationPlugin
|
||||
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||
|
||||
ActionButton {
|
||||
id: root
|
||||
|
||||
readonly property int deviceRotation: RotationPlugin.RotationUtil.deviceRotation
|
||||
readonly property int currentRotation: RotationPlugin.RotationUtil.currentRotation
|
||||
|
||||
iconSource: "rotation-allowed-symbolic"
|
||||
|
||||
// Update button position and timeout when device rotation changes.
|
||||
onDeviceRotationChanged: {
|
||||
if (ShellSettings.Settings.navigationPanelEnabled) return;
|
||||
// reset button if visible
|
||||
if (root.visible) {
|
||||
root.active = false;
|
||||
timeout.stop();
|
||||
}
|
||||
if (!RotationPlugin.RotationUtil.showRotationButton) return;
|
||||
// Position at the bottom left edge of actual device, regardless of current rotation.
|
||||
root.screenCorner = (currentRotation + 1) % 4;
|
||||
// match angle to physical device rotation.
|
||||
root.angle = ((4 + currentRotation - deviceRotation) % 4) * 90;
|
||||
root.active = true;
|
||||
}
|
||||
|
||||
// Rotate to suggested rotation if button is pressed.
|
||||
onTriggered: {
|
||||
root.visible = false;
|
||||
root.active = false;
|
||||
timeout.stop();
|
||||
rotate.restart();
|
||||
}
|
||||
|
||||
// rotate on timeout to give time to hide the button before rotation happens
|
||||
Timer {
|
||||
id: rotate
|
||||
interval: 0
|
||||
repeat: false
|
||||
onTriggered: RotationPlugin.RotationUtil.rotateToSuggestedRotation();
|
||||
}
|
||||
|
||||
// When the button is active, hide it after a certain amount of time has passed.
|
||||
// This is to prevent the button form bothering the user when they do not wish to rotate.
|
||||
onActiveChanged: if (active) timeout.restart();
|
||||
|
||||
Timer {
|
||||
id: timeout
|
||||
interval: 10000
|
||||
repeat: false
|
||||
onTriggered: active = false;
|
||||
}
|
||||
}
|
||||
18
components/rotationplugin/CMakeLists.txt
Normal file
18
components/rotationplugin/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# SPDX-FileCopyrightText: 2025 Micah Stanley <stanleymicah@proton.me>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
ecm_add_qml_module(rotationplugin URI org.kde.plasma.private.mobileshell.rotationplugin GENERATE_PLUGIN_SOURCE)
|
||||
target_sources(rotationplugin PRIVATE rotationutil.cpp)
|
||||
|
||||
target_link_libraries(rotationplugin PRIVATE
|
||||
Qt::Gui
|
||||
Qt::DBus
|
||||
Qt::Qml
|
||||
Qt::Quick
|
||||
Qt::Sensors
|
||||
Plasma::Plasma
|
||||
KF6::Screen
|
||||
Qt::Qml
|
||||
)
|
||||
|
||||
ecm_finalize_qml_module(rotationplugin)
|
||||
158
components/rotationplugin/rotationutil.cpp
Normal file
158
components/rotationplugin/rotationutil.cpp
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Micah Stanley <stanleymicah@proton.me>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "rotationutil.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusPendingReply>
|
||||
#include <QDebug>
|
||||
#include <QGuiApplication>
|
||||
|
||||
#include <kscreen/configmonitor.h>
|
||||
#include <kscreen/getconfigoperation.h>
|
||||
#include <kscreen/output.h>
|
||||
#include <kscreen/setconfigoperation.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
KScreen::Output::Rotation mapReadingOrientation(QOrientationReading::Orientation orientation)
|
||||
{
|
||||
switch (orientation) {
|
||||
case QOrientationReading::Orientation::TopUp:
|
||||
return KScreen::Output::Rotation::None;
|
||||
case QOrientationReading::Orientation::TopDown:
|
||||
return KScreen::Output::Rotation::Inverted;
|
||||
case QOrientationReading::Orientation::LeftUp:
|
||||
return KScreen::Output::Rotation::Left;
|
||||
case QOrientationReading::Orientation::RightUp:
|
||||
return KScreen::Output::Rotation::Right;
|
||||
case QOrientationReading::Orientation::FaceUp:
|
||||
case QOrientationReading::Orientation::FaceDown:
|
||||
case QOrientationReading::Orientation::Undefined:
|
||||
return KScreen::Output::Rotation::None;
|
||||
}
|
||||
return KScreen::Output::Rotation::None;
|
||||
}
|
||||
|
||||
RotationUtil::Rotation mapRotation(KScreen::Output::Rotation rotation)
|
||||
{
|
||||
switch (rotation) {
|
||||
case KScreen::Output::Rotation::Left:
|
||||
return RotationUtil::Rotation::LandscapeLeft;
|
||||
case KScreen::Output::Rotation::Inverted:
|
||||
return RotationUtil::Rotation::UpsideDown;
|
||||
case KScreen::Output::Rotation::Right:
|
||||
return RotationUtil::Rotation::LandscapeRight;
|
||||
default:
|
||||
return RotationUtil::Rotation::Portrait;
|
||||
}
|
||||
}
|
||||
|
||||
RotationUtil::RotationUtil(QObject *parent)
|
||||
: QObject{parent}
|
||||
, m_sensor{new QOrientationSensor(this)}
|
||||
{
|
||||
connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, this, [this](auto *op) {
|
||||
m_config = qobject_cast<KScreen::GetConfigOperation *>(op)->config();
|
||||
KScreen::ConfigMonitor::instance()->addConfig(m_config);
|
||||
|
||||
// update all screens with event connect
|
||||
for (KScreen::OutputPtr output : m_config->outputs()) {
|
||||
connect(output.data(), &KScreen::Output::autoRotatePolicyChanged, this, &RotationUtil::updateShowRotationButton);
|
||||
}
|
||||
|
||||
// listen to all new screens and connect
|
||||
connect(m_config.data(), &KScreen::Config::outputAdded, this, [this](const auto &output) {
|
||||
connect(output.data(), &KScreen::Output::autoRotatePolicyChanged, this, &RotationUtil::updateShowRotationButton);
|
||||
});
|
||||
});
|
||||
|
||||
connect(m_sensor, &QOrientationSensor::readingChanged, this, &RotationUtil::updateShowRotationButton);
|
||||
m_sensor->start();
|
||||
}
|
||||
|
||||
void RotationUtil::rotateToSuggestedRotation()
|
||||
{
|
||||
if (!m_config || !m_showRotationButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto outputs = m_config->outputs();
|
||||
if (outputs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// HACK: Assume the output we care about is the first device
|
||||
for (KScreen::OutputPtr output : outputs) {
|
||||
// apparently it's possible to get nullptr outputs?
|
||||
if (!output) {
|
||||
continue;
|
||||
}
|
||||
|
||||
output->setRotation(m_rotateTo);
|
||||
}
|
||||
|
||||
auto setop = new KScreen::SetConfigOperation(m_config, this);
|
||||
setop->exec();
|
||||
|
||||
updateShowRotationButton();
|
||||
}
|
||||
|
||||
bool RotationUtil::showRotationButton() const
|
||||
{
|
||||
return m_showRotationButton;
|
||||
}
|
||||
|
||||
RotationUtil::Rotation RotationUtil::deviceRotation() const
|
||||
{
|
||||
return m_deviceRotation;
|
||||
}
|
||||
|
||||
RotationUtil::Rotation RotationUtil::currentRotation() const
|
||||
{
|
||||
return m_currentRotation;
|
||||
}
|
||||
|
||||
void RotationUtil::updateShowRotationButton()
|
||||
{
|
||||
if (!m_config) {
|
||||
return;
|
||||
}
|
||||
|
||||
QOrientationReading *reading = m_sensor->reading();
|
||||
if (!reading) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_rotateTo = mapReadingOrientation(reading->orientation());
|
||||
m_deviceRotation = mapRotation(m_rotateTo);
|
||||
|
||||
const auto outputs = m_config->outputs();
|
||||
|
||||
if (outputs.empty()) {
|
||||
m_showRotationButton = false;
|
||||
Q_EMIT rotationChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
// HACK: Assume the output we care about is the first device
|
||||
for (KScreen::OutputPtr output : outputs) {
|
||||
if (!output) {
|
||||
// apparently it's possible to get nullptr outputs?
|
||||
continue;
|
||||
}
|
||||
if (output->autoRotatePolicy() != KScreen::Output::AutoRotatePolicy::Never) {
|
||||
// only check displays that have autorotate on
|
||||
continue;
|
||||
}
|
||||
m_currentRotation = mapRotation(output->rotation());
|
||||
m_showRotationButton = output->rotation() != m_rotateTo;
|
||||
Q_EMIT rotationChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
m_showRotationButton = false;
|
||||
Q_EMIT rotationChanged();
|
||||
}
|
||||
57
components/rotationplugin/rotationutil.h
Normal file
57
components/rotationplugin/rotationutil.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Micah Stanley <stanleymicah@proton.me>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QOrientationSensor>
|
||||
|
||||
#include <kscreen/config.h>
|
||||
#include <qqmlregistration.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class RotationUtil : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
Q_PROPERTY(bool showRotationButton READ showRotationButton NOTIFY rotationChanged)
|
||||
Q_PROPERTY(Rotation deviceRotation READ deviceRotation NOTIFY rotationChanged)
|
||||
Q_PROPERTY(Rotation currentRotation READ currentRotation NOTIFY rotationChanged)
|
||||
|
||||
public:
|
||||
RotationUtil(QObject *parent = nullptr);
|
||||
|
||||
enum Rotation {
|
||||
Portrait = 0,
|
||||
LandscapeLeft,
|
||||
UpsideDown,
|
||||
LandscapeRight
|
||||
};
|
||||
Q_ENUM(Rotation)
|
||||
|
||||
bool showRotationButton() const;
|
||||
Rotation deviceRotation() const;
|
||||
Rotation currentRotation() const;
|
||||
|
||||
Q_INVOKABLE void rotateToSuggestedRotation();
|
||||
|
||||
Q_SIGNALS:
|
||||
void rotationChanged();
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateShowRotationButton();
|
||||
|
||||
private:
|
||||
bool m_showRotationButton{false};
|
||||
KScreen::Output::Rotation m_rotateTo;
|
||||
Rotation m_deviceRotation;
|
||||
Rotation m_currentRotation;
|
||||
|
||||
KScreen::ConfigPtr m_config{nullptr};
|
||||
QOrientationSensor *m_sensor{nullptr};
|
||||
};
|
||||
|
|
@ -141,8 +141,9 @@ ContainmentItem {
|
|||
MobileShellState.ShellDBusObject.registerObject();
|
||||
|
||||
// HACK: we need to initialize the DBus server somewhere, it might as well be here...
|
||||
// initialize the volume osd, and volume keys
|
||||
// initialize notification popups
|
||||
// Initialize the volume osd, and volume keys.
|
||||
// Initialize notification popups.
|
||||
// Initialize action popup buttons.
|
||||
MobileShell.PopupProviderLoader.load();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import org.kde.plasma.private.mobileshell.state as MobileShellState
|
|||
import org.kde.taskmanager as TaskManager
|
||||
import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin
|
||||
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||
import org.kde.plasma.private.mobileshell.rotationplugin as RotationPlugin
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
|
|
@ -105,13 +106,13 @@ MobileShell.NavigationPanel {
|
|||
|
||||
leftCornerAction: MobileShell.NavigationPanelAction {
|
||||
id: rotationAction
|
||||
visible: Plasmoid.showRotationButton
|
||||
visible: RotationPlugin.RotationUtil.showRotationButton
|
||||
enabled: true
|
||||
iconSource: "rotation-allowed-symbolic"
|
||||
iconSizeFactor: 0.75
|
||||
|
||||
onTriggered: {
|
||||
Plasmoid.rotateToSuggestedRotation();
|
||||
RotationPlugin.RotationUtil.rotateToSuggestedRotation();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,54 +11,13 @@
|
|||
#include <QDebug>
|
||||
#include <QGuiApplication>
|
||||
|
||||
#include <kscreen/configmonitor.h>
|
||||
#include <kscreen/getconfigoperation.h>
|
||||
#include <kscreen/output.h>
|
||||
#include <kscreen/setconfigoperation.h>
|
||||
|
||||
// register type for Keyboards.KWinVirtualKeyboard.forceActivate();
|
||||
Q_DECLARE_METATYPE(QDBusPendingReply<>)
|
||||
|
||||
KScreen::Output::Rotation mapReadingOrientation(QOrientationReading::Orientation orientation)
|
||||
{
|
||||
switch (orientation) {
|
||||
case QOrientationReading::Orientation::TopUp:
|
||||
return KScreen::Output::Rotation::None;
|
||||
case QOrientationReading::Orientation::TopDown:
|
||||
return KScreen::Output::Rotation::Inverted;
|
||||
case QOrientationReading::Orientation::LeftUp:
|
||||
return KScreen::Output::Rotation::Left;
|
||||
case QOrientationReading::Orientation::RightUp:
|
||||
return KScreen::Output::Rotation::Right;
|
||||
case QOrientationReading::Orientation::FaceUp:
|
||||
case QOrientationReading::Orientation::FaceDown:
|
||||
case QOrientationReading::Orientation::Undefined:
|
||||
return KScreen::Output::Rotation::None;
|
||||
}
|
||||
return KScreen::Output::Rotation::None;
|
||||
}
|
||||
|
||||
TaskPanel::TaskPanel(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
|
||||
: Plasma::Containment(parent, data, args)
|
||||
, m_sensor{new QOrientationSensor(this)}
|
||||
{
|
||||
connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, this, [this](auto *op) {
|
||||
m_config = qobject_cast<KScreen::GetConfigOperation *>(op)->config();
|
||||
KScreen::ConfigMonitor::instance()->addConfig(m_config);
|
||||
|
||||
// update all screens with event connect
|
||||
for (KScreen::OutputPtr output : m_config->outputs()) {
|
||||
connect(output.data(), &KScreen::Output::autoRotatePolicyChanged, this, &TaskPanel::updateShowRotationButton);
|
||||
}
|
||||
|
||||
// listen to all new screens and connect
|
||||
connect(m_config.data(), &KScreen::Config::outputAdded, this, [this](const auto &output) {
|
||||
connect(output.data(), &KScreen::Output::autoRotatePolicyChanged, this, &TaskPanel::updateShowRotationButton);
|
||||
});
|
||||
});
|
||||
|
||||
connect(m_sensor, &QOrientationSensor::readingChanged, this, &TaskPanel::updateShowRotationButton);
|
||||
m_sensor->start();
|
||||
}
|
||||
|
||||
void TaskPanel::triggerTaskSwitcher() const
|
||||
|
|
@ -70,79 +29,6 @@ void TaskPanel::triggerTaskSwitcher() const
|
|||
QDBusConnection::sessionBus().send(message);
|
||||
}
|
||||
|
||||
void TaskPanel::rotateToSuggestedRotation()
|
||||
{
|
||||
if (!m_config || !m_showRotationButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto outputs = m_config->outputs();
|
||||
if (outputs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// HACK: Assume the output we care about is the first device
|
||||
for (KScreen::OutputPtr output : outputs) {
|
||||
// apparently it's possible to get nullptr outputs?
|
||||
if (!output) {
|
||||
continue;
|
||||
}
|
||||
|
||||
output->setRotation(m_rotateTo);
|
||||
}
|
||||
|
||||
auto setop = new KScreen::SetConfigOperation(m_config, this);
|
||||
setop->exec();
|
||||
|
||||
updateShowRotationButton();
|
||||
}
|
||||
|
||||
bool TaskPanel::showRotationButton() const
|
||||
{
|
||||
return m_showRotationButton;
|
||||
}
|
||||
|
||||
void TaskPanel::updateShowRotationButton()
|
||||
{
|
||||
if (!m_config) {
|
||||
return;
|
||||
}
|
||||
|
||||
QOrientationReading *reading = m_sensor->reading();
|
||||
if (!reading) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_rotateTo = mapReadingOrientation(reading->orientation());
|
||||
|
||||
const auto outputs = m_config->outputs();
|
||||
|
||||
if (outputs.empty()) {
|
||||
m_showRotationButton = false;
|
||||
Q_EMIT showRotationButtonChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
// HACK: Assume the output we care about is the first device
|
||||
for (KScreen::OutputPtr output : outputs) {
|
||||
if (!output) {
|
||||
// apparently it's possible to get nullptr outputs?
|
||||
continue;
|
||||
}
|
||||
if (output->autoRotatePolicy() != KScreen::Output::AutoRotatePolicy::Never) {
|
||||
// only check displays that have autorotate on
|
||||
continue;
|
||||
}
|
||||
|
||||
m_showRotationButton = output->rotation() != m_rotateTo;
|
||||
Q_EMIT showRotationButtonChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
m_showRotationButton = false;
|
||||
Q_EMIT showRotationButtonChanged();
|
||||
}
|
||||
|
||||
K_PLUGIN_CLASS(TaskPanel)
|
||||
|
||||
#include "taskpanel.moc"
|
||||
|
|
|
|||
|
|
@ -7,33 +7,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <Plasma/Containment>
|
||||
#include <QOrientationSensor>
|
||||
|
||||
#include <kscreen/config.h>
|
||||
|
||||
class TaskPanel : public Plasma::Containment
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool showRotationButton READ showRotationButton NOTIFY showRotationButtonChanged)
|
||||
|
||||
public:
|
||||
TaskPanel(QObject *parent, const KPluginMetaData &data, const QVariantList &args);
|
||||
|
||||
Q_INVOKABLE void triggerTaskSwitcher() const;
|
||||
|
||||
bool showRotationButton() const;
|
||||
Q_INVOKABLE void rotateToSuggestedRotation();
|
||||
|
||||
Q_SIGNALS:
|
||||
void showRotationButtonChanged();
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateShowRotationButton();
|
||||
|
||||
private:
|
||||
bool m_showRotationButton{false};
|
||||
KScreen::Output::Rotation m_rotateTo;
|
||||
|
||||
KScreen::ConfigPtr m_config{nullptr};
|
||||
QOrientationSensor *m_sensor{nullptr};
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue