mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-06-11 08:57:21 +00:00
Add profile-aware initial setup
Detect the device class, stage the selected experience, and write the resulting setup choices through SetupState. Load the new device and experience modules before the existing setup pages, and use the Shift icon on the finished page.
This commit is contained in:
parent
1b8efc3be3
commit
42d41351e2
19 changed files with 1000 additions and 151 deletions
|
|
@ -7,6 +7,10 @@ add_subdirectory(modules)
|
||||||
|
|
||||||
add_executable(plasma-mobile-initial-start
|
add_executable(plasma-mobile-initial-start
|
||||||
main.cpp
|
main.cpp
|
||||||
|
devicecontext.cpp
|
||||||
|
devicecontext.h
|
||||||
|
setupstate.cpp
|
||||||
|
setupstate.h
|
||||||
wizard.cpp
|
wizard.cpp
|
||||||
wizard.h
|
wizard.h
|
||||||
settings.cpp
|
settings.cpp
|
||||||
|
|
|
||||||
158
initialstart/devicecontext.cpp
Normal file
158
initialstart/devicecontext.cpp
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Marco Allegretti
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
#include "devicecontext.h"
|
||||||
|
|
||||||
|
#include <KRuntimePlatform>
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QInputDevice>
|
||||||
|
#include <QJSEngine>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QScreen>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool pathExists(const QString &path)
|
||||||
|
{
|
||||||
|
return QFileInfo::exists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool systemHasBattery()
|
||||||
|
{
|
||||||
|
const QDir powerSupply(QStringLiteral("/sys/class/power_supply"));
|
||||||
|
const QStringList entries = powerSupply.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
for (const QString &entry : entries) {
|
||||||
|
if (entry.startsWith(QStringLiteral("BAT"), Qt::CaseInsensitive)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (pathExists(powerSupply.filePath(entry + QStringLiteral("/type")))) {
|
||||||
|
QFile typeFile(powerSupply.filePath(entry + QStringLiteral("/type")));
|
||||||
|
if (typeFile.open(QIODevice::ReadOnly) && QString::fromUtf8(typeFile.readAll()).trimmed() == QLatin1String("Battery")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceContext::DeviceContext(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_runtimePlatform(KRuntimePlatform::runtimePlatform().join(QLatin1Char(',')))
|
||||||
|
, m_hasBattery(systemHasBattery())
|
||||||
|
{
|
||||||
|
const QList<const QInputDevice *> devices = QInputDevice::devices();
|
||||||
|
for (const QInputDevice *device : devices) {
|
||||||
|
if (!device) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto type = device->type();
|
||||||
|
m_hasTouch = m_hasTouch || type == QInputDevice::DeviceType::TouchScreen;
|
||||||
|
m_hasKeyboard = m_hasKeyboard || type == QInputDevice::DeviceType::Keyboard;
|
||||||
|
m_hasMouse = m_hasMouse || type == QInputDevice::DeviceType::Mouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QList<QScreen *> screens = QGuiApplication::screens();
|
||||||
|
m_displayCount = std::max(1, static_cast<int>(screens.size()));
|
||||||
|
if (QScreen *screen = QGuiApplication::primaryScreen()) {
|
||||||
|
const QSize size = screen->geometry().size();
|
||||||
|
m_primaryDisplayLandscape = size.width() >= size.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString platform = m_runtimePlatform;
|
||||||
|
if (m_displayCount > 1) {
|
||||||
|
m_recommendedDeviceClass = QStringLiteral("dual-screen");
|
||||||
|
m_recommendedExperienceProfile = QStringLiteral("hybrid");
|
||||||
|
} else if (platform.contains(QStringLiteral("phone"))) {
|
||||||
|
m_recommendedDeviceClass = QStringLiteral("phone");
|
||||||
|
m_recommendedExperienceProfile = QStringLiteral("mobile");
|
||||||
|
} else if (platform.contains(QStringLiteral("handheld"))) {
|
||||||
|
m_recommendedDeviceClass = QStringLiteral("handheld");
|
||||||
|
m_recommendedExperienceProfile = QStringLiteral("gaming");
|
||||||
|
} else if (platform.contains(QStringLiteral("tablet"))) {
|
||||||
|
m_recommendedDeviceClass = QStringLiteral("tablet");
|
||||||
|
m_recommendedExperienceProfile = QStringLiteral("mobile");
|
||||||
|
} else if (m_hasBattery && m_hasTouch && (m_hasKeyboard || m_hasMouse)) {
|
||||||
|
m_recommendedDeviceClass = QStringLiteral("tablet");
|
||||||
|
m_recommendedExperienceProfile = QStringLiteral("hybrid");
|
||||||
|
} else if (m_hasBattery && (m_hasKeyboard || m_hasMouse)) {
|
||||||
|
m_recommendedDeviceClass = QStringLiteral("laptop");
|
||||||
|
m_recommendedExperienceProfile = QStringLiteral("desktop");
|
||||||
|
} else if (!m_hasBattery && !m_hasTouch) {
|
||||||
|
m_recommendedDeviceClass = QStringLiteral("desktop");
|
||||||
|
m_recommendedExperienceProfile = QStringLiteral("desktop");
|
||||||
|
} else {
|
||||||
|
m_recommendedDeviceClass = QStringLiteral("tablet");
|
||||||
|
m_recommendedExperienceProfile = QStringLiteral("mobile");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceContext *DeviceContext::self()
|
||||||
|
{
|
||||||
|
static auto *instance = new DeviceContext(qApp);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceContext *DeviceContext::create(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
|
||||||
|
{
|
||||||
|
Q_UNUSED(qmlEngine)
|
||||||
|
Q_UNUSED(jsEngine)
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DeviceContext::runtimePlatform() const
|
||||||
|
{
|
||||||
|
return m_runtimePlatform;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DeviceContext::recommendedDeviceClass() const
|
||||||
|
{
|
||||||
|
return m_recommendedDeviceClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DeviceContext::recommendedExperienceProfile() const
|
||||||
|
{
|
||||||
|
return m_recommendedExperienceProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceContext::hasTouch() const
|
||||||
|
{
|
||||||
|
return m_hasTouch;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceContext::hasKeyboard() const
|
||||||
|
{
|
||||||
|
return m_hasKeyboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceContext::hasMouse() const
|
||||||
|
{
|
||||||
|
return m_hasMouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceContext::hasBattery() const
|
||||||
|
{
|
||||||
|
return m_hasBattery;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DeviceContext::displayCount() const
|
||||||
|
{
|
||||||
|
return m_displayCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceContext::hasExternalDisplay() const
|
||||||
|
{
|
||||||
|
return m_displayCount > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceContext::primaryDisplayLandscape() const
|
||||||
|
{
|
||||||
|
return m_primaryDisplayLandscape;
|
||||||
|
}
|
||||||
57
initialstart/devicecontext.h
Normal file
57
initialstart/devicecontext.h
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Marco Allegretti
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <qqmlregistration.h>
|
||||||
|
|
||||||
|
class QQmlEngine;
|
||||||
|
class QJSEngine;
|
||||||
|
|
||||||
|
class DeviceContext : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
QML_SINGLETON
|
||||||
|
|
||||||
|
Q_PROPERTY(QString runtimePlatform READ runtimePlatform CONSTANT)
|
||||||
|
Q_PROPERTY(QString recommendedDeviceClass READ recommendedDeviceClass CONSTANT)
|
||||||
|
Q_PROPERTY(QString recommendedExperienceProfile READ recommendedExperienceProfile CONSTANT)
|
||||||
|
Q_PROPERTY(bool hasTouch READ hasTouch CONSTANT)
|
||||||
|
Q_PROPERTY(bool hasKeyboard READ hasKeyboard CONSTANT)
|
||||||
|
Q_PROPERTY(bool hasMouse READ hasMouse CONSTANT)
|
||||||
|
Q_PROPERTY(bool hasBattery READ hasBattery CONSTANT)
|
||||||
|
Q_PROPERTY(int displayCount READ displayCount CONSTANT)
|
||||||
|
Q_PROPERTY(bool hasExternalDisplay READ hasExternalDisplay CONSTANT)
|
||||||
|
Q_PROPERTY(bool primaryDisplayLandscape READ primaryDisplayLandscape CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DeviceContext(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
static DeviceContext *self();
|
||||||
|
static DeviceContext *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine);
|
||||||
|
|
||||||
|
QString runtimePlatform() const;
|
||||||
|
QString recommendedDeviceClass() const;
|
||||||
|
QString recommendedExperienceProfile() const;
|
||||||
|
bool hasTouch() const;
|
||||||
|
bool hasKeyboard() const;
|
||||||
|
bool hasMouse() const;
|
||||||
|
bool hasBattery() const;
|
||||||
|
int displayCount() const;
|
||||||
|
bool hasExternalDisplay() const;
|
||||||
|
bool primaryDisplayLandscape() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_runtimePlatform;
|
||||||
|
QString m_recommendedDeviceClass;
|
||||||
|
QString m_recommendedExperienceProfile;
|
||||||
|
bool m_hasTouch = false;
|
||||||
|
bool m_hasKeyboard = false;
|
||||||
|
bool m_hasMouse = false;
|
||||||
|
bool m_hasBattery = false;
|
||||||
|
int m_displayCount = 1;
|
||||||
|
bool m_primaryDisplayLandscape = false;
|
||||||
|
};
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
|
#include <QDebug>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
|
|
@ -12,6 +13,7 @@
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "setupstate.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "wizard.h"
|
#include "wizard.h"
|
||||||
|
|
||||||
|
|
@ -19,6 +21,11 @@ std::unique_ptr<QCommandLineParser> createParser()
|
||||||
{
|
{
|
||||||
auto parser = std::make_unique<QCommandLineParser>();
|
auto parser = std::make_unique<QCommandLineParser>();
|
||||||
parser->addOption(QCommandLineOption(QStringLiteral("test-wizard"), i18n("Opens the initial start wizard without modifying configuration")));
|
parser->addOption(QCommandLineOption(QStringLiteral("test-wizard"), i18n("Opens the initial start wizard without modifying configuration")));
|
||||||
|
parser->addOption(QCommandLineOption(QStringLiteral("test-apply-defaults"),
|
||||||
|
i18n("Writes recommended setup defaults and exits without applying them to the running session")));
|
||||||
|
parser->addOption(QCommandLineOption(QStringLiteral("test-apply-profile"),
|
||||||
|
i18n("Writes setup defaults for a profile and exits without applying them to the running session"),
|
||||||
|
i18n("profile")));
|
||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,14 +51,32 @@ int main(int argc, char *argv[])
|
||||||
aboutData.processCommandLine(parser.get());
|
aboutData.processCommandLine(parser.get());
|
||||||
|
|
||||||
const bool testWizard = parser->isSet(QStringLiteral("test-wizard"));
|
const bool testWizard = parser->isSet(QStringLiteral("test-wizard"));
|
||||||
|
const bool testApplyDefaults = parser->isSet(QStringLiteral("test-apply-defaults"));
|
||||||
|
const QString testApplyProfile = parser->value(QStringLiteral("test-apply-profile"));
|
||||||
|
if (testApplyDefaults || !testApplyProfile.isEmpty()) {
|
||||||
|
if (!testApplyProfile.isEmpty()) {
|
||||||
|
const QStringList supportedProfiles{QStringLiteral("mobile"), QStringLiteral("desktop"), QStringLiteral("gaming"), QStringLiteral("hybrid")};
|
||||||
|
if (!supportedProfiles.contains(testApplyProfile)) {
|
||||||
|
qCritical() << "Unsupported setup profile" << testApplyProfile;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
SetupState::self()->applyExperienceDefaults(testApplyProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupState::self()->apply(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!testWizard) {
|
if (!testWizard) {
|
||||||
// if the wizard has already been run, or we aren't in plasma mobile
|
// if the wizard has already been run with a setup profile
|
||||||
if (!Settings::self()->shouldStartWizard()) {
|
if (!Settings::self()->shouldStartWizard()) {
|
||||||
qDebug() << "Wizard will not be started since either it has already been run, or the current session is not Plasma Mobile.";
|
qDebug() << "Wizard will not be started since it has already been run.";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.setWindowIcon(QIcon::fromTheme(QStringLiteral("start-here-shift")));
|
||||||
|
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
engine.rootContext()->setContextObject(new KLocalizedContext{&engine});
|
engine.rootContext()->setContextObject(new KLocalizedContext{&engine});
|
||||||
|
|
||||||
|
|
@ -63,9 +88,7 @@ int main(int argc, char *argv[])
|
||||||
return wizard;
|
return wizard;
|
||||||
});
|
});
|
||||||
|
|
||||||
engine.load(QUrl(QStringLiteral("qrc:org/kde/plasma/mobileinitialstart/initialstart/qml/Main.qml")));
|
engine.load(QUrl(QStringLiteral("qrc:/org/kde/plasma/mobileinitialstart/initialstart/qml/Main.qml")));
|
||||||
|
|
||||||
app.setWindowIcon(QIcon::fromTheme(QStringLiteral("start-here-symbolic")));
|
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
# SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
|
# SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
plasma_install_package(deviceprofile org.kde.plasma.mobileinitialstart.deviceprofile mobileinitialstart)
|
||||||
|
plasma_install_package(experienceprofile org.kde.plasma.mobileinitialstart.experienceprofile mobileinitialstart)
|
||||||
plasma_install_package(finished org.kde.plasma.mobileinitialstart.finished mobileinitialstart)
|
plasma_install_package(finished org.kde.plasma.mobileinitialstart.finished mobileinitialstart)
|
||||||
plasma_install_package(systemnavigation org.kde.plasma.mobileinitialstart.systemnavigation mobileinitialstart)
|
plasma_install_package(systemnavigation org.kde.plasma.mobileinitialstart.systemnavigation mobileinitialstart)
|
||||||
add_subdirectory(cellular)
|
add_subdirectory(cellular)
|
||||||
|
|
|
||||||
174
initialstart/modules/deviceprofile/contents/ui/main.qml
Normal file
174
initialstart/modules/deviceprofile/contents/ui/main.qml
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Marco Allegretti
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
|
import org.kde.plasma.mobileinitialstart.initialstart
|
||||||
|
|
||||||
|
InitialStartModule {
|
||||||
|
name: i18n("Device")
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property real cardWidth: Math.min(Kirigami.Units.gridUnit * 30, root.width - Kirigami.Units.gridUnit * 2)
|
||||||
|
|
||||||
|
function chooseDevice(deviceClass, primaryInput) {
|
||||||
|
SetupState.applyDeviceDefaults(deviceClass, primaryInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedDeviceLabel() {
|
||||||
|
switch (SetupState.deviceClass) {
|
||||||
|
case "phone":
|
||||||
|
return i18n("Phone")
|
||||||
|
case "tablet":
|
||||||
|
return i18n("Tablet or 2-in-1")
|
||||||
|
case "laptop":
|
||||||
|
return i18n("Laptop")
|
||||||
|
case "desktop":
|
||||||
|
return i18n("Desktop PC")
|
||||||
|
case "handheld":
|
||||||
|
return i18n("Gaming handheld")
|
||||||
|
case "mini-pc":
|
||||||
|
return i18n("Gaming PC")
|
||||||
|
case "dual-screen":
|
||||||
|
return i18n("Dual-screen device")
|
||||||
|
case "foldable":
|
||||||
|
return i18n("Foldable device")
|
||||||
|
default:
|
||||||
|
return SetupState.deviceClass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedInputLabel() {
|
||||||
|
switch (SetupState.primaryInput) {
|
||||||
|
case "touch":
|
||||||
|
return i18n("touch")
|
||||||
|
case "keyboardMouse":
|
||||||
|
return i18n("keyboard and pointer")
|
||||||
|
case "gamepad":
|
||||||
|
return i18n("gamepad")
|
||||||
|
default:
|
||||||
|
return SetupState.primaryInput
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
topMargin: Kirigami.Units.gridUnit
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
contentWidth: -1
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: root.width
|
||||||
|
spacing: Kirigami.Units.gridUnit
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.leftMargin: Kirigami.Units.gridUnit
|
||||||
|
Layout.rightMargin: Kirigami.Units.gridUnit
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
Layout.fillWidth: true
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
text: i18n("What are you setting up SHIFT on?")
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormCard {
|
||||||
|
maximumWidth: root.cardWidth
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||||
|
|
||||||
|
FormCard.FormTextDelegate {
|
||||||
|
text: i18n("Current selection")
|
||||||
|
description: i18n("%1, controlled with %2", root.selectedDeviceLabel(), root.selectedInputLabel())
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormButtonDelegate {
|
||||||
|
text: i18n("Use Hardware Recommendation")
|
||||||
|
icon.name: "emblem-ok-symbolic"
|
||||||
|
onClicked: SetupState.useRecommendedSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormDelegateSeparator {}
|
||||||
|
|
||||||
|
FormCard.FormRadioDelegate {
|
||||||
|
text: i18n("Phone")
|
||||||
|
description: i18n("Pocket touch device. Starts with the simple mobile home screen and full-screen apps.")
|
||||||
|
onClicked: root.chooseDevice("phone", "touch")
|
||||||
|
|
||||||
|
Binding on checked {
|
||||||
|
value: SetupState.deviceClass === "phone"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormRadioDelegate {
|
||||||
|
text: i18n("Tablet or 2-in-1")
|
||||||
|
description: i18n("Touch device that may rotate, dock, or use a keyboard. Starts with adaptive defaults.")
|
||||||
|
onClicked: root.chooseDevice("tablet", "touch")
|
||||||
|
|
||||||
|
Binding on checked {
|
||||||
|
value: SetupState.deviceClass === "tablet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormRadioDelegate {
|
||||||
|
text: i18n("Laptop or Desktop")
|
||||||
|
description: i18n("Keyboard and pointer computer. Starts with windows, Overview, dock, tiling, and snap layouts.")
|
||||||
|
onClicked: root.chooseDevice(DeviceContext.hasBattery ? "laptop" : "desktop", "keyboardMouse")
|
||||||
|
|
||||||
|
Binding on checked {
|
||||||
|
value: SetupState.deviceClass === "laptop" || SetupState.deviceClass === "desktop"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormRadioDelegate {
|
||||||
|
text: i18n("Gaming PC or Handheld")
|
||||||
|
description: i18n("Gamepad-first setup for a handheld, console-style mini PC, or living-room gaming device.")
|
||||||
|
onClicked: root.chooseDevice(DeviceContext.hasBattery ? "handheld" : "mini-pc", "gamepad")
|
||||||
|
|
||||||
|
Binding on checked {
|
||||||
|
value: SetupState.deviceClass === "handheld" || SetupState.deviceClass === "mini-pc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormRadioDelegate {
|
||||||
|
text: i18n("Dual-screen or Foldable")
|
||||||
|
description: i18n("Hardware whose screen layout changes by posture, hinge, external display, or dock.")
|
||||||
|
onClicked: root.chooseDevice(DeviceContext.displayCount > 1 ? "dual-screen" : "foldable", SetupState.primaryInput)
|
||||||
|
|
||||||
|
Binding on checked {
|
||||||
|
value: SetupState.deviceClass === "dual-screen" || SetupState.deviceClass === "foldable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormCard {
|
||||||
|
maximumWidth: root.cardWidth
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||||
|
|
||||||
|
FormCard.FormTextDelegate {
|
||||||
|
text: i18n("Detected context")
|
||||||
|
description: i18n("Recommended: %1 with %2 layout. Displays: %3, Touch: %4, Battery: %5",
|
||||||
|
DeviceContext.recommendedDeviceClass,
|
||||||
|
DeviceContext.recommendedExperienceProfile,
|
||||||
|
DeviceContext.displayCount,
|
||||||
|
DeviceContext.hasTouch ? i18n("yes") : i18n("no"),
|
||||||
|
DeviceContext.hasBattery ? i18n("yes") : i18n("no"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
initialstart/modules/deviceprofile/metadata.json
Normal file
16
initialstart/modules/deviceprofile/metadata.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"KPackageStructure": "KPackage/GenericQML",
|
||||||
|
"KPlugin": {
|
||||||
|
"Authors": [
|
||||||
|
{
|
||||||
|
"Email": "",
|
||||||
|
"Name": "Marco Allegretti"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Description": "Device profile initial setup module for SHIFT",
|
||||||
|
"Id": "org.kde.plasma.mobileinitialstart.deviceprofile",
|
||||||
|
"License": "EUPL-1.2",
|
||||||
|
"Name": "Device Profile",
|
||||||
|
"Website": "https://kde.org"
|
||||||
|
}
|
||||||
|
}
|
||||||
125
initialstart/modules/experienceprofile/contents/ui/main.qml
Normal file
125
initialstart/modules/experienceprofile/contents/ui/main.qml
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Marco Allegretti
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
|
import org.kde.plasma.mobileinitialstart.initialstart
|
||||||
|
|
||||||
|
InitialStartModule {
|
||||||
|
name: i18n("Experience")
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property real cardWidth: Math.min(Kirigami.Units.gridUnit * 30, root.width - Kirigami.Units.gridUnit * 2)
|
||||||
|
|
||||||
|
function profileLabel(profile) {
|
||||||
|
switch (profile) {
|
||||||
|
case "mobile":
|
||||||
|
return i18n("Touch home")
|
||||||
|
case "desktop":
|
||||||
|
return i18n("Desktop windows")
|
||||||
|
case "gaming":
|
||||||
|
return i18n("Gamepad gaming")
|
||||||
|
case "hybrid":
|
||||||
|
return i18n("Adaptive docked")
|
||||||
|
default:
|
||||||
|
return profile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
topMargin: Kirigami.Units.gridUnit
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
contentWidth: -1
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: root.width
|
||||||
|
spacing: Kirigami.Units.gridUnit
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.leftMargin: Kirigami.Units.gridUnit
|
||||||
|
Layout.rightMargin: Kirigami.Units.gridUnit
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
Layout.fillWidth: true
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
text: i18n("Choose how SHIFT should start after setup.")
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormCard {
|
||||||
|
maximumWidth: root.cardWidth
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||||
|
|
||||||
|
FormCard.FormRadioDelegate {
|
||||||
|
text: i18n("Touch home")
|
||||||
|
description: i18n("Maximized apps for phones and small tablets.")
|
||||||
|
onClicked: SetupState.applyExperienceDefaults("mobile")
|
||||||
|
|
||||||
|
Binding on checked {
|
||||||
|
value: SetupState.experienceProfile === "mobile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormRadioDelegate {
|
||||||
|
text: i18n("Desktop windows")
|
||||||
|
description: i18n("Windows, tiling, dock, and Overview.")
|
||||||
|
onClicked: SetupState.applyExperienceDefaults("desktop")
|
||||||
|
|
||||||
|
Binding on checked {
|
||||||
|
value: SetupState.experienceProfile === "desktop"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormRadioDelegate {
|
||||||
|
text: i18n("Gamepad gaming")
|
||||||
|
description: i18n("Game Center layout for handheld PCs.")
|
||||||
|
onClicked: SetupState.applyExperienceDefaults("gaming")
|
||||||
|
|
||||||
|
Binding on checked {
|
||||||
|
value: SetupState.experienceProfile === "gaming"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormRadioDelegate {
|
||||||
|
text: i18n("Adaptive docked")
|
||||||
|
description: i18n("Auto-hide panels for tablets and foldables.")
|
||||||
|
onClicked: SetupState.applyExperienceDefaults("hybrid")
|
||||||
|
|
||||||
|
Binding on checked {
|
||||||
|
value: SetupState.experienceProfile === "hybrid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormCard {
|
||||||
|
maximumWidth: root.cardWidth
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||||
|
|
||||||
|
FormCard.FormTextDelegate {
|
||||||
|
text: i18n("Current selection")
|
||||||
|
description: i18n("%1. Windows: %2, Gaming: %3, Tiling: %4",
|
||||||
|
root.profileLabel(SetupState.experienceProfile),
|
||||||
|
SetupState.convergenceModeEnabled ? i18n("on") : i18n("off"),
|
||||||
|
SetupState.gamingModeEnabled ? i18n("on") : i18n("off"),
|
||||||
|
SetupState.dynamicTilingEnabled ? i18n("on") : i18n("off"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
initialstart/modules/experienceprofile/metadata.json
Normal file
16
initialstart/modules/experienceprofile/metadata.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"KPackageStructure": "KPackage/GenericQML",
|
||||||
|
"KPlugin": {
|
||||||
|
"Authors": [
|
||||||
|
{
|
||||||
|
"Email": "",
|
||||||
|
"Name": "Marco Allegretti"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Description": "Experience profile initial setup module for SHIFT",
|
||||||
|
"Id": "org.kde.plasma.mobileinitialstart.experienceprofile",
|
||||||
|
"License": "EUPL-1.2",
|
||||||
|
"Name": "Experience Profile",
|
||||||
|
"Website": "https://kde.org"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,27 +14,29 @@ InitialStartModule {
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Kirigami.Units.gridUnit
|
anchors.margins: Kirigami.Units.gridUnit
|
||||||
|
spacing: Kirigami.Units.gridUnit
|
||||||
|
|
||||||
|
Item { Layout.fillHeight: true }
|
||||||
|
|
||||||
|
Kirigami.Icon {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
|
||||||
|
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
||||||
|
source: "start-here-shift"
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: i18n("Your device is now ready. <br /><br />Enjoy <b>%1</b>!", InitialStartUtil.distroName)
|
text: i18n("Your device is ready. <br /><br />SHIFT will start with the <b>%1</b> experience.", SetupState.experienceProfile)
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true }
|
Item { Layout.fillHeight: true }
|
||||||
|
|
||||||
Image {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
source: "konqi-calling.png"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
import org.kde.kirigamiaddons.formcard as FormCard
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
import org.kde.plasma.mobileinitialstart.initialstart
|
import org.kde.plasma.mobileinitialstart.initialstart
|
||||||
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
|
||||||
|
|
||||||
InitialStartModule {
|
InitialStartModule {
|
||||||
name: i18n("System Navigation")
|
name: i18n("System Navigation")
|
||||||
|
|
@ -49,14 +48,14 @@ InitialStartModule {
|
||||||
text: i18n("Gesture navigation")
|
text: i18n("Gesture navigation")
|
||||||
description: i18n("Swipe up from the bottom to see running applications. Flick to go to the home screen.")
|
description: i18n("Swipe up from the bottom to see running applications. Flick to go to the home screen.")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (checked && ShellSettings.Settings.navigationPanelEnabled) {
|
if (checked && SetupState.navigationPanelEnabled) {
|
||||||
ShellSettings.Settings.navigationPanelEnabled = false;
|
SetupState.navigationPanelEnabled = false;
|
||||||
}
|
}
|
||||||
checked = Qt.binding(function () { return !ShellSettings.Settings.navigationPanelEnabled; });
|
checked = Qt.binding(function () { return !SetupState.navigationPanelEnabled; });
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding on checked {
|
Binding on checked {
|
||||||
value: !ShellSettings.Settings.navigationPanelEnabled
|
value: !SetupState.navigationPanelEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -70,14 +69,14 @@ InitialStartModule {
|
||||||
text: i18n("Button navigation")
|
text: i18n("Button navigation")
|
||||||
description: i18n("Use buttons on a navigation bar to navigate the system.")
|
description: i18n("Use buttons on a navigation bar to navigate the system.")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (checked && !ShellSettings.Settings.navigationPanelEnabled) {
|
if (checked && !SetupState.navigationPanelEnabled) {
|
||||||
ShellSettings.Settings.navigationPanelEnabled = true;
|
SetupState.navigationPanelEnabled = true;
|
||||||
}
|
}
|
||||||
checked = Qt.binding(function () { return ShellSettings.Settings.navigationPanelEnabled; });
|
checked = Qt.binding(function () { return SetupState.navigationPanelEnabled; });
|
||||||
}
|
}
|
||||||
|
|
||||||
Binding on checked {
|
Binding on checked {
|
||||||
value: ShellSettings.Settings.navigationPanelEnabled
|
value: SetupState.navigationPanelEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,21 +102,20 @@ Item {
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: i18n("Welcome to<br/><b>Plasma Mobile</b>")
|
text: i18n("Welcome to<br/><b>SHIFT</b>")
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
font.pointSize: 18
|
font.pointSize: 18
|
||||||
color: "white"
|
color: "white"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
opacity: root.contentOpacity
|
opacity: root.contentOpacity
|
||||||
spacing: Kirigami.Units.largeSpacing
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
right: parent.right
|
right: parent.right
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
|
|
@ -125,10 +124,9 @@ Item {
|
||||||
bottomMargin: Kirigami.Units.gridUnit * 2
|
bottomMargin: Kirigami.Units.gridUnit * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Kirigami.Heading {
|
Kirigami.Heading {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: i18n("Powered by<br/><b>%1</b>", InitialStartUtil.distroName)
|
text: i18n("Built on<br/><b>%1</b>", InitialStartUtil.distroName)
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQuick.Window 2.15
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
|
|
@ -12,11 +13,13 @@ import initialstart 1.0 as InitialStart
|
||||||
Kirigami.ApplicationWindow {
|
Kirigami.ApplicationWindow {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
width: 360
|
minimumWidth: Kirigami.Units.gridUnit * 20
|
||||||
height: 720
|
minimumHeight: Kirigami.Units.gridUnit * 28
|
||||||
|
width: Math.min(Screen.width, Math.max(Kirigami.Units.gridUnit * 24, Screen.width * 0.42))
|
||||||
|
height: Math.min(Screen.height, Math.max(Kirigami.Units.gridUnit * 36, Screen.height * 0.78))
|
||||||
visibility: "Windowed"
|
visibility: "Windowed"
|
||||||
|
|
||||||
title: i18n("Initial Start")
|
title: i18n("SHIFT Initial Setup")
|
||||||
|
|
||||||
pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.None
|
pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.None
|
||||||
pageStack.columnView.columnResizeMode: Kirigami.ColumnView.SingleColumn
|
pageStack.columnView.columnResizeMode: Kirigami.ColumnView.SingleColumn
|
||||||
|
|
|
||||||
|
|
@ -29,42 +29,25 @@ Kirigami.Page {
|
||||||
|
|
||||||
readonly property bool onFinalPage: currentIndex === (stepCount - 1)
|
readonly property bool onFinalPage: currentIndex === (stepCount - 1)
|
||||||
|
|
||||||
|
function updateStepItems() {
|
||||||
|
if (stepRepeater.count === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.previousStepItem = currentIndex > 0 ? stepRepeater.itemAt(currentIndex - 1) : null;
|
||||||
|
root.currentStepItem = stepRepeater.itemAt(currentIndex);
|
||||||
|
root.nextStepItem = currentIndex < stepRepeater.count - 1 ? stepRepeater.itemAt(currentIndex + 1) : null;
|
||||||
|
}
|
||||||
|
|
||||||
// step animation
|
// step animation
|
||||||
// manually doing the animation is more performant and less glitchy with window resize than a SwipeView
|
// manually doing the animation is more performant and less glitchy with window resize than a SwipeView
|
||||||
property real previousStepItemX: 0
|
property real previousStepItemX: -root.width
|
||||||
property real currentStepItemX: 0
|
property real currentStepItemX: 0
|
||||||
property real nextStepItemX: 0
|
property real nextStepItemX: root.width
|
||||||
|
|
||||||
NumberAnimation on previousStepItemX {
|
|
||||||
id: previousStepAnim
|
|
||||||
duration: 400
|
|
||||||
easing.type: Easing.OutExpo
|
|
||||||
onFinished: {
|
|
||||||
if (root.previousStepItemX != 0) {
|
|
||||||
root.previousStepItem.visible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation on currentStepItemX {
|
|
||||||
id: currentStepAnim
|
|
||||||
duration: 400
|
|
||||||
easing.type: Easing.OutExpo
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation on nextStepItemX {
|
|
||||||
id: nextStepAnim
|
|
||||||
duration: 400
|
|
||||||
easing.type: Easing.OutExpo
|
|
||||||
onFinished: {
|
|
||||||
if (root.nextStepItemX != 0) {
|
|
||||||
root.nextStepItem.visible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onStepCountChanged: {
|
onStepCountChanged: {
|
||||||
// reset position
|
// reset position
|
||||||
|
updateStepItems();
|
||||||
requestPreviousPage();
|
requestPreviousPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,54 +57,43 @@ Kirigami.Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
function requestNextPage() {
|
function requestNextPage() {
|
||||||
if (previousStepAnim.running || currentStepAnim.running || nextStepAnim.running) {
|
if (currentIndex >= stepCount - 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
previousStepItemX = 0;
|
|
||||||
|
|
||||||
currentIndex++;
|
currentIndex++;
|
||||||
|
updateStepItems();
|
||||||
stepHeading.changeText(currentStepItem.name);
|
stepHeading.changeText(currentStepItem.name);
|
||||||
|
|
||||||
currentStepItemX = root.width;
|
previousStepItemX = -root.width;
|
||||||
currentStepItem.visible = true;
|
currentStepItemX = 0;
|
||||||
|
nextStepItemX = root.width;
|
||||||
previousStepAnim.to = -root.width;
|
|
||||||
previousStepAnim.restart();
|
|
||||||
currentStepAnim.to = 0;
|
|
||||||
currentStepAnim.restart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function requestPreviousPage() {
|
function requestPreviousPage() {
|
||||||
if (previousStepAnim.running || currentStepAnim.running || nextStepAnim.running) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentIndex === 0) {
|
if (currentIndex === 0) {
|
||||||
root.showingLanding = true;
|
root.showingLanding = true;
|
||||||
landingComponent.returnToLanding();
|
landingComponent.returnToLanding();
|
||||||
} else {
|
} else {
|
||||||
nextStepItemX = 0;
|
|
||||||
|
|
||||||
currentIndex--;
|
currentIndex--;
|
||||||
|
updateStepItems();
|
||||||
stepHeading.changeText(currentStepItem.name);
|
stepHeading.changeText(currentStepItem.name);
|
||||||
|
|
||||||
currentStepItemX = -root.width;
|
previousStepItemX = -root.width;
|
||||||
currentStepItem.visible = true;
|
currentStepItemX = 0;
|
||||||
|
nextStepItemX = root.width;
|
||||||
nextStepAnim.to = root.width;
|
|
||||||
nextStepAnim.restart();
|
|
||||||
currentStepAnim.to = 0;
|
|
||||||
currentStepAnim.restart();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LandingComponent {
|
LandingComponent {
|
||||||
id: landingComponent
|
id: landingComponent
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
enabled: root.showingLanding
|
||||||
|
visible: root.showingLanding
|
||||||
|
|
||||||
onRequestNextPage: {
|
onRequestNextPage: {
|
||||||
root.showingLanding = false;
|
root.showingLanding = false;
|
||||||
|
root.updateStepItems();
|
||||||
stepHeading.changeText(root.currentStepItem.name);
|
stepHeading.changeText(root.currentStepItem.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -136,20 +108,6 @@ Kirigami.Page {
|
||||||
x: 0
|
x: 0
|
||||||
y: root.showingLanding ? overlaySteps.height : 0
|
y: root.showingLanding ? overlaySteps.height : 0
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 1000
|
|
||||||
easing.type: Easing.OutExpo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on y {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 1000
|
|
||||||
easing.type: Easing.OutExpo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// heading for all the wizard steps
|
// heading for all the wizard steps
|
||||||
Label {
|
Label {
|
||||||
id: stepHeading
|
id: stepHeading
|
||||||
|
|
@ -168,24 +126,8 @@ Kirigami.Page {
|
||||||
property string toText
|
property string toText
|
||||||
|
|
||||||
function changeText(text) {
|
function changeText(text) {
|
||||||
toText = text;
|
stepHeading.text = text;
|
||||||
toHidden.restart();
|
stepHeading.opacity = 1;
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation on opacity {
|
|
||||||
id: toHidden
|
|
||||||
duration: 200
|
|
||||||
to: 0
|
|
||||||
onFinished: {
|
|
||||||
stepHeading.text = stepHeading.toText;
|
|
||||||
toShown.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation on opacity {
|
|
||||||
id: toShown
|
|
||||||
duration: 200
|
|
||||||
to: 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,11 +155,12 @@ Kirigami.Page {
|
||||||
|
|
||||||
// setup steps
|
// setup steps
|
||||||
Repeater {
|
Repeater {
|
||||||
|
id: stepRepeater
|
||||||
model: InitialStart.Wizard.steps
|
model: InitialStart.Wizard.steps
|
||||||
|
|
||||||
delegate: MobileShell.BaseItem {
|
delegate: MobileShell.BaseItem {
|
||||||
id: item
|
id: item
|
||||||
visible: model.index === 0 // the binding is broken later
|
visible: !root.showingLanding && Math.abs(item.currentIndex - root.currentIndex) <= 1
|
||||||
contentItem: modelData.contentItem
|
contentItem: modelData.contentItem
|
||||||
transform: Translate {
|
transform: Translate {
|
||||||
x: {
|
x: {
|
||||||
|
|
@ -238,27 +181,8 @@ Kirigami.Page {
|
||||||
property string name: modelData.name
|
property string name: modelData.name
|
||||||
property int currentIndex: model.index
|
property int currentIndex: model.index
|
||||||
|
|
||||||
function updateRootItems() {
|
|
||||||
if (model.index === root.currentIndex) {
|
|
||||||
root.currentStepItem = item;
|
|
||||||
} else if (model.index === root.currentIndex - 1) {
|
|
||||||
root.previousStepItem = item;
|
|
||||||
} else if (model.index === root.currentIndex + 1) {
|
|
||||||
root.nextStepItem = item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
updateRootItems();
|
root.updateStepItems();
|
||||||
}
|
|
||||||
|
|
||||||
// keep root properties updated
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
|
|
||||||
function onCurrentIndexChanged() {
|
|
||||||
item.updateRootItems();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
#include <KConfigGroup>
|
#include <KConfigGroup>
|
||||||
#include <KRuntimePlatform>
|
|
||||||
|
|
||||||
const QString CONFIG_FILE = QStringLiteral("plasmamobilerc");
|
const QString CONFIG_FILE = QStringLiteral("plasmamobilerc");
|
||||||
const QString INITIAL_START_CONFIG_GROUP = QStringLiteral("InitialStart");
|
const QString INITIAL_START_CONFIG_GROUP = QStringLiteral("InitialStart");
|
||||||
|
|
@ -12,18 +11,14 @@ const QString INITIAL_START_CONFIG_GROUP = QStringLiteral("InitialStart");
|
||||||
Settings::Settings(QObject *parent)
|
Settings::Settings(QObject *parent)
|
||||||
: QObject{parent}
|
: QObject{parent}
|
||||||
, m_mobileConfig{KSharedConfig::openConfig(CONFIG_FILE)}
|
, m_mobileConfig{KSharedConfig::openConfig(CONFIG_FILE)}
|
||||||
, m_isMobilePlatform{KRuntimePlatform::runtimePlatform().contains(QStringLiteral("phone"))}
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::shouldStartWizard()
|
bool Settings::shouldStartWizard()
|
||||||
{
|
{
|
||||||
if (!m_isMobilePlatform) {
|
const KConfigGroup initialStartGroup{m_mobileConfig, INITIAL_START_CONFIG_GROUP};
|
||||||
return false;
|
const KConfigGroup generalGroup{m_mobileConfig, QStringLiteral("General")};
|
||||||
}
|
return !initialStartGroup.readEntry("wizardRun", false) || generalGroup.readEntry("setupExperienceProfile", QString()).isEmpty();
|
||||||
|
|
||||||
auto group = KConfigGroup{m_mobileConfig, INITIAL_START_CONFIG_GROUP};
|
|
||||||
return !group.readEntry("wizardRun", false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::setWizardFinished()
|
void Settings::setWizardFinished()
|
||||||
|
|
|
||||||
|
|
@ -18,5 +18,4 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
KSharedConfig::Ptr m_mobileConfig;
|
KSharedConfig::Ptr m_mobileConfig;
|
||||||
bool m_isMobilePlatform;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
257
initialstart/setupstate.cpp
Normal file
257
initialstart/setupstate.cpp
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Marco Allegretti
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
#include "setupstate.h"
|
||||||
|
#include "devicecontext.h"
|
||||||
|
|
||||||
|
#include <KConfigGroup>
|
||||||
|
#include <KSharedConfig>
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QJSEngine>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
|
||||||
|
const QString CONFIG_FILE = QStringLiteral("plasmamobilerc");
|
||||||
|
const QString GENERAL_CONFIG_GROUP = QStringLiteral("General");
|
||||||
|
const QString INITIAL_START_CONFIG_GROUP = QStringLiteral("InitialStart");
|
||||||
|
|
||||||
|
SetupState::SetupState(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
useRecommendedSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupState *SetupState::self()
|
||||||
|
{
|
||||||
|
static auto *instance = new SetupState(qApp);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupState *SetupState::create(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
|
||||||
|
{
|
||||||
|
Q_UNUSED(qmlEngine)
|
||||||
|
Q_UNUSED(jsEngine)
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SetupState::deviceClass() const
|
||||||
|
{
|
||||||
|
return m_deviceClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::setDeviceClass(const QString &deviceClass)
|
||||||
|
{
|
||||||
|
if (m_deviceClass == deviceClass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_deviceClass = deviceClass;
|
||||||
|
Q_EMIT deviceClassChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SetupState::experienceProfile() const
|
||||||
|
{
|
||||||
|
return m_experienceProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::setExperienceProfile(const QString &experienceProfile)
|
||||||
|
{
|
||||||
|
if (m_experienceProfile == experienceProfile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_experienceProfile = experienceProfile;
|
||||||
|
Q_EMIT experienceProfileChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SetupState::primaryInput() const
|
||||||
|
{
|
||||||
|
return m_primaryInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::setPrimaryInput(const QString &primaryInput)
|
||||||
|
{
|
||||||
|
if (m_primaryInput == primaryInput) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_primaryInput = primaryInput;
|
||||||
|
Q_EMIT primaryInputChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetupState::convergenceModeEnabled() const
|
||||||
|
{
|
||||||
|
return m_convergenceModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::setConvergenceModeEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (m_convergenceModeEnabled == enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_convergenceModeEnabled = enabled;
|
||||||
|
emitShellSettingsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetupState::gamingModeEnabled() const
|
||||||
|
{
|
||||||
|
return m_gamingModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::setGamingModeEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (m_gamingModeEnabled == enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_gamingModeEnabled = enabled;
|
||||||
|
emitShellSettingsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetupState::navigationPanelEnabled() const
|
||||||
|
{
|
||||||
|
return m_navigationPanelEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::setNavigationPanelEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (m_navigationPanelEnabled == enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_navigationPanelEnabled = enabled;
|
||||||
|
emitShellSettingsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetupState::dynamicTilingEnabled() const
|
||||||
|
{
|
||||||
|
return m_dynamicTilingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::setDynamicTilingEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (m_dynamicTilingEnabled == enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_dynamicTilingEnabled = enabled;
|
||||||
|
emitShellSettingsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetupState::snapLayoutsEnabled() const
|
||||||
|
{
|
||||||
|
return m_snapLayoutsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::setSnapLayoutsEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (m_snapLayoutsEnabled == enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_snapLayoutsEnabled = enabled;
|
||||||
|
emitShellSettingsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetupState::autoHidePanelsEnabled() const
|
||||||
|
{
|
||||||
|
return m_autoHidePanelsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::setAutoHidePanelsEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (m_autoHidePanelsEnabled == enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_autoHidePanelsEnabled = enabled;
|
||||||
|
emitShellSettingsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::useRecommendedSettings()
|
||||||
|
{
|
||||||
|
auto *context = DeviceContext::self();
|
||||||
|
setDeviceClass(context->recommendedDeviceClass());
|
||||||
|
|
||||||
|
if (context->recommendedExperienceProfile() == QLatin1String("gaming")) {
|
||||||
|
setPrimaryInput(QStringLiteral("gamepad"));
|
||||||
|
} else if (context->hasTouch() && !context->hasKeyboard() && !context->hasMouse()) {
|
||||||
|
setPrimaryInput(QStringLiteral("touch"));
|
||||||
|
} else {
|
||||||
|
setPrimaryInput(QStringLiteral("keyboardMouse"));
|
||||||
|
}
|
||||||
|
|
||||||
|
applyExperienceDefaults(context->recommendedExperienceProfile());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::applyDeviceDefaults(const QString &deviceClass, const QString &primaryInput)
|
||||||
|
{
|
||||||
|
setDeviceClass(deviceClass);
|
||||||
|
setPrimaryInput(primaryInput);
|
||||||
|
|
||||||
|
if (deviceClass == QLatin1String("handheld") || deviceClass == QLatin1String("mini-pc")) {
|
||||||
|
applyExperienceDefaults(QStringLiteral("gaming"));
|
||||||
|
} else if (deviceClass == QLatin1String("laptop") || deviceClass == QLatin1String("desktop")) {
|
||||||
|
applyExperienceDefaults(QStringLiteral("desktop"));
|
||||||
|
} else if (deviceClass == QLatin1String("dual-screen") || deviceClass == QLatin1String("foldable")) {
|
||||||
|
applyExperienceDefaults(QStringLiteral("hybrid"));
|
||||||
|
} else {
|
||||||
|
applyExperienceDefaults(QStringLiteral("mobile"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::applyExperienceDefaults(const QString &experienceProfile)
|
||||||
|
{
|
||||||
|
setExperienceProfile(experienceProfile);
|
||||||
|
|
||||||
|
if (experienceProfile == QLatin1String("gaming")) {
|
||||||
|
setConvergenceModeEnabled(false);
|
||||||
|
setGamingModeEnabled(true);
|
||||||
|
setNavigationPanelEnabled(false);
|
||||||
|
setDynamicTilingEnabled(false);
|
||||||
|
setSnapLayoutsEnabled(false);
|
||||||
|
setAutoHidePanelsEnabled(true);
|
||||||
|
} else if (experienceProfile == QLatin1String("desktop")) {
|
||||||
|
setConvergenceModeEnabled(true);
|
||||||
|
setGamingModeEnabled(false);
|
||||||
|
setNavigationPanelEnabled(false);
|
||||||
|
setDynamicTilingEnabled(true);
|
||||||
|
setSnapLayoutsEnabled(true);
|
||||||
|
setAutoHidePanelsEnabled(false);
|
||||||
|
} else if (experienceProfile == QLatin1String("hybrid")) {
|
||||||
|
setConvergenceModeEnabled(true);
|
||||||
|
setGamingModeEnabled(false);
|
||||||
|
setNavigationPanelEnabled(false);
|
||||||
|
setDynamicTilingEnabled(true);
|
||||||
|
setSnapLayoutsEnabled(true);
|
||||||
|
setAutoHidePanelsEnabled(true);
|
||||||
|
} else {
|
||||||
|
setConvergenceModeEnabled(false);
|
||||||
|
setGamingModeEnabled(false);
|
||||||
|
setNavigationPanelEnabled(false);
|
||||||
|
setDynamicTilingEnabled(false);
|
||||||
|
setSnapLayoutsEnabled(false);
|
||||||
|
setAutoHidePanelsEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::apply(bool applyToRunningSession)
|
||||||
|
{
|
||||||
|
auto config = KSharedConfig::openConfig(CONFIG_FILE);
|
||||||
|
KConfigGroup general(config, GENERAL_CONFIG_GROUP);
|
||||||
|
general.writeEntry("setupDeviceClass", m_deviceClass, KConfigGroup::Notify);
|
||||||
|
general.writeEntry("setupExperienceProfile", m_experienceProfile, KConfigGroup::Notify);
|
||||||
|
general.writeEntry("setupPrimaryInput", m_primaryInput, KConfigGroup::Notify);
|
||||||
|
general.writeEntry("convergenceModeEnabled", m_convergenceModeEnabled, KConfigGroup::Notify);
|
||||||
|
general.writeEntry("gamingModeEnabled", m_gamingModeEnabled, KConfigGroup::Notify);
|
||||||
|
general.writeEntry("navigationPanelEnabled", m_navigationPanelEnabled, KConfigGroup::Notify);
|
||||||
|
general.writeEntry("dynamicTilingEnabled", m_dynamicTilingEnabled, KConfigGroup::Notify);
|
||||||
|
general.writeEntry("snapLayoutsEnabled", m_snapLayoutsEnabled, KConfigGroup::Notify);
|
||||||
|
general.writeEntry("autoHidePanelsEnabled", m_autoHidePanelsEnabled, KConfigGroup::Notify);
|
||||||
|
|
||||||
|
KConfigGroup initialStart(config, INITIAL_START_CONFIG_GROUP);
|
||||||
|
initialStart.writeEntry("wizardRun", true, KConfigGroup::Notify);
|
||||||
|
config->sync();
|
||||||
|
|
||||||
|
if (applyToRunningSession) {
|
||||||
|
QProcess::startDetached(QStringLiteral("plasma-mobile-envmanager"), {QStringLiteral("--apply-settings")});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupState::emitShellSettingsChanged()
|
||||||
|
{
|
||||||
|
Q_EMIT shellSettingsChanged();
|
||||||
|
}
|
||||||
85
initialstart/setupstate.h
Normal file
85
initialstart/setupstate.h
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Marco Allegretti
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <qqmlregistration.h>
|
||||||
|
|
||||||
|
class QQmlEngine;
|
||||||
|
class QJSEngine;
|
||||||
|
|
||||||
|
class SetupState : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
QML_SINGLETON
|
||||||
|
|
||||||
|
Q_PROPERTY(QString deviceClass READ deviceClass WRITE setDeviceClass NOTIFY deviceClassChanged)
|
||||||
|
Q_PROPERTY(QString experienceProfile READ experienceProfile WRITE setExperienceProfile NOTIFY experienceProfileChanged)
|
||||||
|
Q_PROPERTY(QString primaryInput READ primaryInput WRITE setPrimaryInput NOTIFY primaryInputChanged)
|
||||||
|
Q_PROPERTY(bool convergenceModeEnabled READ convergenceModeEnabled WRITE setConvergenceModeEnabled NOTIFY shellSettingsChanged)
|
||||||
|
Q_PROPERTY(bool gamingModeEnabled READ gamingModeEnabled WRITE setGamingModeEnabled NOTIFY shellSettingsChanged)
|
||||||
|
Q_PROPERTY(bool navigationPanelEnabled READ navigationPanelEnabled WRITE setNavigationPanelEnabled NOTIFY shellSettingsChanged)
|
||||||
|
Q_PROPERTY(bool dynamicTilingEnabled READ dynamicTilingEnabled WRITE setDynamicTilingEnabled NOTIFY shellSettingsChanged)
|
||||||
|
Q_PROPERTY(bool snapLayoutsEnabled READ snapLayoutsEnabled WRITE setSnapLayoutsEnabled NOTIFY shellSettingsChanged)
|
||||||
|
Q_PROPERTY(bool autoHidePanelsEnabled READ autoHidePanelsEnabled WRITE setAutoHidePanelsEnabled NOTIFY shellSettingsChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SetupState(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
static SetupState *self();
|
||||||
|
static SetupState *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine);
|
||||||
|
|
||||||
|
QString deviceClass() const;
|
||||||
|
void setDeviceClass(const QString &deviceClass);
|
||||||
|
|
||||||
|
QString experienceProfile() const;
|
||||||
|
void setExperienceProfile(const QString &experienceProfile);
|
||||||
|
|
||||||
|
QString primaryInput() const;
|
||||||
|
void setPrimaryInput(const QString &primaryInput);
|
||||||
|
|
||||||
|
bool convergenceModeEnabled() const;
|
||||||
|
void setConvergenceModeEnabled(bool enabled);
|
||||||
|
|
||||||
|
bool gamingModeEnabled() const;
|
||||||
|
void setGamingModeEnabled(bool enabled);
|
||||||
|
|
||||||
|
bool navigationPanelEnabled() const;
|
||||||
|
void setNavigationPanelEnabled(bool enabled);
|
||||||
|
|
||||||
|
bool dynamicTilingEnabled() const;
|
||||||
|
void setDynamicTilingEnabled(bool enabled);
|
||||||
|
|
||||||
|
bool snapLayoutsEnabled() const;
|
||||||
|
void setSnapLayoutsEnabled(bool enabled);
|
||||||
|
|
||||||
|
bool autoHidePanelsEnabled() const;
|
||||||
|
void setAutoHidePanelsEnabled(bool enabled);
|
||||||
|
|
||||||
|
Q_INVOKABLE void useRecommendedSettings();
|
||||||
|
Q_INVOKABLE void applyDeviceDefaults(const QString &deviceClass, const QString &primaryInput);
|
||||||
|
Q_INVOKABLE void applyExperienceDefaults(const QString &experienceProfile);
|
||||||
|
void apply(bool applyToRunningSession = true);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void deviceClassChanged();
|
||||||
|
void experienceProfileChanged();
|
||||||
|
void primaryInputChanged();
|
||||||
|
void shellSettingsChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void emitShellSettingsChanged();
|
||||||
|
|
||||||
|
QString m_deviceClass;
|
||||||
|
QString m_experienceProfile;
|
||||||
|
QString m_primaryInput;
|
||||||
|
bool m_convergenceModeEnabled = false;
|
||||||
|
bool m_gamingModeEnabled = false;
|
||||||
|
bool m_navigationPanelEnabled = false;
|
||||||
|
bool m_dynamicTilingEnabled = true;
|
||||||
|
bool m_snapLayoutsEnabled = true;
|
||||||
|
bool m_autoHidePanelsEnabled = false;
|
||||||
|
};
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "wizard.h"
|
#include "wizard.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "setupstate.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <KPackage/PackageLoader>
|
#include <KPackage/PackageLoader>
|
||||||
|
|
@ -11,15 +12,24 @@
|
||||||
#include <QQmlComponent>
|
#include <QQmlComponent>
|
||||||
|
|
||||||
// TODO read distro provided config file
|
// TODO read distro provided config file
|
||||||
const QList<QString> WIZARD_MODULE_ORDER = {QStringLiteral("org.kde.plasma.mobileinitialstart.prepare"),
|
const QList<QString> WIZARD_MODULE_ORDER = {QStringLiteral("org.kde.plasma.mobileinitialstart.deviceprofile"),
|
||||||
|
QStringLiteral("org.kde.plasma.mobileinitialstart.experienceprofile"),
|
||||||
|
QStringLiteral("org.kde.plasma.mobileinitialstart.prepare"),
|
||||||
QStringLiteral("org.kde.plasma.mobileinitialstart.time"),
|
QStringLiteral("org.kde.plasma.mobileinitialstart.time"),
|
||||||
QStringLiteral("org.kde.plasma.mobileinitialstart.wifi"),
|
QStringLiteral("org.kde.plasma.mobileinitialstart.wifi"),
|
||||||
QStringLiteral("org.kde.plasma.mobileinitialstart.cellular"),
|
QStringLiteral("org.kde.plasma.mobileinitialstart.cellular"),
|
||||||
QStringLiteral("org.kde.plasma.mobileinitialstart.systemnavigation"),
|
QStringLiteral("org.kde.plasma.mobileinitialstart.systemnavigation"),
|
||||||
QStringLiteral("org.kde.plasma.mobileinitialstart.finished")};
|
QStringLiteral("org.kde.plasma.mobileinitialstart.finished")};
|
||||||
|
|
||||||
|
int moduleOrderIndex(const QString &pluginId)
|
||||||
|
{
|
||||||
|
const int index = WIZARD_MODULE_ORDER.indexOf(pluginId);
|
||||||
|
return index == -1 ? WIZARD_MODULE_ORDER.size() : index;
|
||||||
|
}
|
||||||
|
|
||||||
Wizard::Wizard(QObject *parent, QQmlEngine *engine)
|
Wizard::Wizard(QObject *parent, QQmlEngine *engine)
|
||||||
: QObject{parent}
|
: QObject{parent}
|
||||||
|
, m_testingMode{false}
|
||||||
, m_engine{engine}
|
, m_engine{engine}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +55,7 @@ void Wizard::load()
|
||||||
|
|
||||||
// sort modules by order
|
// sort modules by order
|
||||||
std::sort(m_modulePackages.begin(), m_modulePackages.end(), [](const auto &lhs, const auto &rhs) {
|
std::sort(m_modulePackages.begin(), m_modulePackages.end(), [](const auto &lhs, const auto &rhs) {
|
||||||
return WIZARD_MODULE_ORDER.indexOf(lhs.first->pluginId()) < WIZARD_MODULE_ORDER.indexOf(rhs.first->pluginId());
|
return moduleOrderIndex(lhs.first->pluginId()) < moduleOrderIndex(rhs.first->pluginId());
|
||||||
});
|
});
|
||||||
|
|
||||||
QQmlComponent *c = new QQmlComponent(m_engine, this);
|
QQmlComponent *c = new QQmlComponent(m_engine, this);
|
||||||
|
|
@ -105,7 +115,9 @@ int Wizard::stepsCount()
|
||||||
|
|
||||||
void Wizard::wizardFinished()
|
void Wizard::wizardFinished()
|
||||||
{
|
{
|
||||||
Settings::self()->setWizardFinished();
|
if (!m_testingMode) {
|
||||||
|
SetupState::self()->apply();
|
||||||
|
}
|
||||||
QCoreApplication::quit();
|
QCoreApplication::quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue