initialstart: Introduce InitialStartModule as top level item for modules

Currently modules are initialized as QQuickItems. In order to be able to
add custom properties for modules to set in the future, introduce
InitialStartModule as the top-level QML object for modules to
initialize.

Currently only two properties are implemented: `available` for whether
to show the module in the wizard, and `contentItem` for the visual
module item.
This commit is contained in:
Devin Lin 2024-11-06 22:07:50 -08:00
parent 2a5dec9cf0
commit e66d88a754
11 changed files with 537 additions and 404 deletions

View file

@ -14,6 +14,8 @@ add_executable(plasma-mobile-initial-start
utils.h utils.h
initialstartutil.cpp initialstartutil.cpp
initialstartutil.h initialstartutil.h
initialstartmodule.cpp
initialstartmodule.h
) )
qt_add_qml_module(plasma-mobile-initial-start qt_add_qml_module(plasma-mobile-initial-start

View file

@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: 2024 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "initialstartmodule.h"
InitialStartModule::InitialStartModule(QObject *parent)
: QObject{parent}
{
}
bool InitialStartModule::available() const
{
return m_available;
}
void InitialStartModule::setAvailable(bool available)
{
if (m_available == available) {
return;
}
m_available = available;
Q_EMIT availableChanged();
}
QQuickItem *InitialStartModule::contentItem()
{
return m_contentItem;
}
void InitialStartModule::setContentItem(QQuickItem *contentItem)
{
if (m_contentItem == contentItem) {
return;
}
m_contentItem = contentItem;
Q_EMIT contentItemChanged();
}
QQmlListProperty<QObject> InitialStartModule::children()
{
return QQmlListProperty<QObject>(this, &m_children);
}

View file

@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: 2024 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once
#include "qqml.h"
#include <QAbstractListModel>
#include <QQmlListProperty>
#include <QQuickItem>
class InitialStartModule : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(bool available READ available WRITE setAvailable NOTIFY availableChanged)
Q_PROPERTY(QQuickItem *contentItem READ contentItem WRITE setContentItem REQUIRED NOTIFY contentItemChanged)
Q_PROPERTY(QQmlListProperty<QObject> children READ children CONSTANT)
Q_CLASSINFO("DefaultProperty", "children")
public:
InitialStartModule(QObject *parent = nullptr);
bool available() const;
void setAvailable(bool available);
QQuickItem *contentItem();
void setContentItem(QQuickItem *contentItem);
QQmlListProperty<QObject> children();
Q_SIGNALS:
void availableChanged();
void contentItemChanged();
private:
bool m_available{true};
QQuickItem *m_contentItem{nullptr};
QList<QObject *> m_children;
};

View file

@ -9,129 +9,133 @@ import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard 1 as FormCard import org.kde.kirigamiaddons.formcard 1 as FormCard
import org.kde.plasma.mm as PlasmaMM import org.kde.plasma.mm as PlasmaMM
Item { import org.kde.plasma.mobileinitialstart.initialstart
id: root
property string name: i18n("Cellular")
readonly property real cardWidth: Math.min(Kirigami.Units.gridUnit * 30, root.width - Kirigami.Units.gridUnit * 2) InitialStartModule {
contentItem: Item {
id: root
property string name: i18n("Cellular")
function toggleMobileData() { readonly property real cardWidth: Math.min(Kirigami.Units.gridUnit * 30, root.width - Kirigami.Units.gridUnit * 2)
if (PlasmaMM.SignalIndicator.needsAPNAdded || !PlasmaMM.SignalIndicator.mobileDataSupported) {
// open settings if unable to toggle mobile data
MobileShell.ShellUtil.executeCommand("plasma-open-settings kcm_cellular_network");
} else {
PlasmaMM.SignalIndicator.mobileDataEnabled = !PlasmaMM.SignalIndicator.mobileDataEnabled;
}
}
EditProfileDialog { function toggleMobileData() {
id: profileDialog if (PlasmaMM.SignalIndicator.needsAPNAdded || !PlasmaMM.SignalIndicator.mobileDataSupported) {
profile: null // open settings if unable to toggle mobile data
} MobileShell.ShellUtil.executeCommand("plasma-open-settings kcm_cellular_network");
} else {
ColumnLayout { PlasmaMM.SignalIndicator.mobileDataEnabled = !PlasmaMM.SignalIndicator.mobileDataEnabled;
anchors {
fill: parent
topMargin: Kirigami.Units.gridUnit
bottomMargin: Kirigami.Units.largeSpacing
}
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
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
text: {
if (!PlasmaMM.SignalIndicator.modemAvailable) {
return i18n("Your device does not have a modem available.");
} else if (PlasmaMM.SignalIndicator.needsAPNAdded) {
return i18n("Please configure your APN below for mobile data, further information will be available with your carrier.");
} else if (PlasmaMM.SignalIndicator.mobileDataSupported) {
return i18n("You are connected to the mobile network.");
} else if (PlasmaMM.SignalIndicator.simEmpty) {
return i18n("Please insert a SIM card into your device.");
} else {
return i18n("Your device does not have a modem available.");
}
} }
} }
FormCard.FormCard { EditProfileDialog {
visible: PlasmaMM.SignalIndicator.modemAvailable && PlasmaMM.SignalIndicator.mobileDataSupported id: profileDialog
maximumWidth: root.cardWidth profile: null
}
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter ColumnLayout {
anchors {
fill: parent
topMargin: Kirigami.Units.gridUnit
bottomMargin: Kirigami.Units.largeSpacing
}
width: root.width
spacing: Kirigami.Units.gridUnit
FormCard.FormSwitchDelegate { Label {
text: i18n("Mobile Data") Layout.leftMargin: Kirigami.Units.gridUnit
checked: PlasmaMM.SignalIndicator.mobileDataEnabled Layout.rightMargin: Kirigami.Units.gridUnit
onCheckedChanged: { Layout.alignment: Qt.AlignTop
if (checked !== PlasmaMM.SignalIndicator.mobileDataEnabled) { Layout.fillWidth: true
root.toggleMobileData();
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
text: {
if (!PlasmaMM.SignalIndicator.modemAvailable) {
return i18n("Your device does not have a modem available.");
} else if (PlasmaMM.SignalIndicator.needsAPNAdded) {
return i18n("Please configure your APN below for mobile data, further information will be available with your carrier.");
} else if (PlasmaMM.SignalIndicator.mobileDataSupported) {
return i18n("You are connected to the mobile network.");
} else if (PlasmaMM.SignalIndicator.simEmpty) {
return i18n("Please insert a SIM card into your device.");
} else {
return i18n("Your device does not have a modem available.");
} }
} }
} }
}
FormCard.FormCard { FormCard.FormCard {
visible: PlasmaMM.SignalIndicator.modemAvailable && !PlasmaMM.SignalIndicator.simEmpty visible: PlasmaMM.SignalIndicator.modemAvailable && PlasmaMM.SignalIndicator.mobileDataSupported
maximumWidth: root.cardWidth maximumWidth: root.cardWidth
Layout.fillHeight: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
ListView {
id: listView
currentIndex: -1
clip: true
Layout.fillWidth: true
Layout.fillHeight: true
model: PlasmaMM.SignalIndicator.profiles
delegate: FormCard.FormRadioDelegate {
width: listView.width
text: modelData.name
description: modelData.apn
checked: modem.activeConnectionUni == modelData.connectionUni
FormCard.FormSwitchDelegate {
text: i18n("Mobile Data")
checked: PlasmaMM.SignalIndicator.mobileDataEnabled
onCheckedChanged: { onCheckedChanged: {
if (checked) { if (checked !== PlasmaMM.SignalIndicator.mobileDataEnabled) {
PlasmaMM.SignalIndicator.activateProfile(modelData.connectionUni); root.toggleMobileData();
checked = Qt.binding(() => { return modem.activeConnectionUni == modelData.connectionUni });
} }
} }
}
}
trailing: RowLayout { FormCard.FormCard {
ToolButton { visible: PlasmaMM.SignalIndicator.modemAvailable && !PlasmaMM.SignalIndicator.simEmpty
icon.name: "entry-edit" maximumWidth: root.cardWidth
text: i18n("Edit")
onClicked: { Layout.fillHeight: true
profileDialog.profile = modelData; Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
profileDialog.open();
ListView {
id: listView
currentIndex: -1
clip: true
Layout.fillWidth: true
Layout.fillHeight: true
model: PlasmaMM.SignalIndicator.profiles
delegate: FormCard.FormRadioDelegate {
width: listView.width
text: modelData.name
description: modelData.apn
checked: modem.activeConnectionUni == modelData.connectionUni
onCheckedChanged: {
if (checked) {
PlasmaMM.SignalIndicator.activateProfile(modelData.connectionUni);
checked = Qt.binding(() => { return modem.activeConnectionUni == modelData.connectionUni });
} }
} }
ToolButton {
icon.name: "delete" trailing: RowLayout {
text: i18n("Delete") ToolButton {
onClicked: PlasmaMM.SignalIndicator.removeProfile(modelData.connectionUni) icon.name: "entry-edit"
text: i18n("Edit")
onClicked: {
profileDialog.profile = modelData;
profileDialog.open();
}
}
ToolButton {
icon.name: "delete"
text: i18n("Delete")
onClicked: PlasmaMM.SignalIndicator.removeProfile(modelData.connectionUni)
}
} }
} }
} }
}
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
icon.name: "list-add" icon.name: "list-add"
text: i18n("Add APN") text: i18n("Add APN")
onClicked: { onClicked: {
profileDialog.profile = null; profileDialog.profile = null;
profileDialog.open(); profileDialog.open();
}
} }
} }
} }

View file

@ -9,31 +9,32 @@ import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.mobileinitialstart.initialstart import org.kde.plasma.mobileinitialstart.initialstart
Item { InitialStartModule {
id: root contentItem: Item {
id: root
property string name: i18n("Complete!") property string name: i18n("Complete!")
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: Kirigami.Units.gridUnit anchors.margins: Kirigami.Units.gridUnit
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
text: i18n("Your device is now ready. <br /><br />Enjoy <b>%1</b>!", InitialStartUtil.distroName) text: i18n("Your device is now ready. <br /><br />Enjoy <b>%1</b>!", InitialStartUtil.distroName)
wrapMode: Text.Wrap wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
} }
Item { Layout.fillHeight: true } Item { Layout.fillHeight: true }
Image { Image {
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: "konqi-calling.png" source: "konqi-calling.png"
}
} }
} }
} }

View file

@ -10,132 +10,137 @@ import org.kde.kirigamiaddons.formcard 1.0 as FormCard
import org.kde.plasma.mobileinitialstart.prepare 1.0 as Prepare import org.kde.plasma.mobileinitialstart.prepare 1.0 as Prepare
import org.kde.plasma.private.mobileshell.screenbrightnessplugin as ScreenBrightness import org.kde.plasma.private.mobileshell.screenbrightnessplugin as ScreenBrightness
Item { import org.kde.plasma.mobileinitialstart.initialstart
id: root
property string name: i18n("Before we get started…")
readonly property real cardWidth: Math.min(Kirigami.Units.gridUnit * 30, root.width - Kirigami.Units.gridUnit * 2) InitialStartModule {
id: module
contentItem: Item {
id: root
property string name: i18n("Before we get started…")
ScreenBrightness.ScreenBrightnessUtil { readonly property real cardWidth: Math.min(Kirigami.Units.gridUnit * 30, root.width - Kirigami.Units.gridUnit * 2)
id: screenBrightness
}
ScrollView { ScreenBrightness.ScreenBrightnessUtil {
anchors { id: screenBrightness
fill: parent
topMargin: Kirigami.Units.gridUnit
} }
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ScrollView {
contentWidth: -1 anchors {
fill: parent
ColumnLayout { topMargin: Kirigami.Units.gridUnit
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
visible: screenBrightness.brightnessAvailable
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
text: i18n("Adjust the screen brightness to be comfortable for the installation process.")
} }
FormCard.FormCard { ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
id: brightnessCard contentWidth: -1
visible: screenBrightness.brightnessAvailable
maximumWidth: root.cardWidth
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter ColumnLayout {
width: root.width
spacing: Kirigami.Units.gridUnit
FormCard.AbstractFormDelegate { Label {
background: null Layout.leftMargin: Kirigami.Units.gridUnit
Layout.rightMargin: Kirigami.Units.gridUnit
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
contentItem: RowLayout { visible: screenBrightness.brightnessAvailable
spacing: Kirigami.Units.gridUnit wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
text: i18n("Adjust the screen brightness to be comfortable for the installation process.")
}
Kirigami.Icon { FormCard.FormCard {
implicitWidth: Kirigami.Units.iconSizes.smallMedium id: brightnessCard
implicitHeight: Kirigami.Units.iconSizes.smallMedium visible: screenBrightness.brightnessAvailable
source: "brightness-low" maximumWidth: root.cardWidth
}
Slider { Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
id: brightnessSlider
Layout.fillWidth: true
from: 1
to: screenBrightness.maxBrightness
value: screenBrightness.brightness
onMoved: screenBrightness.brightness = value;
// HACK: for some reason, the slider initial value doesn't set without being done after the component completes loading FormCard.AbstractFormDelegate {
Timer { background: null
interval: 0
running: true contentItem: RowLayout {
repeat: false spacing: Kirigami.Units.gridUnit
onTriggered: brightnessSlider.value = Qt.binding(() => screenBrightness.brightness)
Kirigami.Icon {
implicitWidth: Kirigami.Units.iconSizes.smallMedium
implicitHeight: Kirigami.Units.iconSizes.smallMedium
source: "brightness-low"
}
Slider {
id: brightnessSlider
Layout.fillWidth: true
from: 1
to: screenBrightness.maxBrightness
value: screenBrightness.brightness
onMoved: screenBrightness.brightness = value;
// HACK: for some reason, the slider initial value doesn't set without being done after the component completes loading
Timer {
interval: 0
running: true
repeat: false
onTriggered: brightnessSlider.value = Qt.binding(() => screenBrightness.brightness)
}
}
Kirigami.Icon {
implicitWidth: Kirigami.Units.iconSizes.smallMedium
implicitHeight: Kirigami.Units.iconSizes.smallMedium
source: "brightness-high"
} }
} }
Kirigami.Icon {
implicitWidth: Kirigami.Units.iconSizes.smallMedium
implicitHeight: Kirigami.Units.iconSizes.smallMedium
source: "brightness-high"
}
} }
} }
}
Label { Label {
Layout.leftMargin: Kirigami.Units.gridUnit Layout.leftMargin: Kirigami.Units.gridUnit
Layout.rightMargin: Kirigami.Units.gridUnit Layout.rightMargin: Kirigami.Units.gridUnit
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
Layout.fillWidth: true Layout.fillWidth: true
wrapMode: Text.Wrap wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
text: i18n("Adjust the size of elements on the screen.") text: i18n("Adjust the size of elements on the screen.")
}
FormCard.FormCard {
id: scalingCard
maximumWidth: root.cardWidth
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
FormCard.FormComboBoxDelegate {
id: displayScaling
text: i18n("Display Scaling")
displayMode: FormCard.FormComboBoxDelegate.Dialog
currentIndex: Prepare.PrepareUtil.scalingOptions.indexOf(Prepare.PrepareUtil.scaling.toString() + "%");
model: Prepare.PrepareUtil.scalingOptions
// remove % suffix
onCurrentValueChanged: Prepare.PrepareUtil.scaling = parseInt(currentValue.substring(0, currentValue.length - 1));
} }
}
FormCard.FormCard { FormCard.FormCard {
id: darkThemeCard id: scalingCard
maximumWidth: root.cardWidth maximumWidth: root.cardWidth
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
FormCard.FormSwitchDelegate { FormCard.FormComboBoxDelegate {
id: darkThemeSwitch id: displayScaling
text: i18n("Dark Theme") text: i18n("Display Scaling")
checked: Prepare.PrepareUtil.usingDarkTheme displayMode: FormCard.FormComboBoxDelegate.Dialog
onCheckedChanged: { currentIndex: Prepare.PrepareUtil.scalingOptions.indexOf(Prepare.PrepareUtil.scaling.toString() + "%");
if (checked !== Prepare.PrepareUtil.usingDarkTheme) { model: Prepare.PrepareUtil.scalingOptions
Prepare.PrepareUtil.usingDarkTheme = checked;
// remove % suffix
onCurrentValueChanged: Prepare.PrepareUtil.scaling = parseInt(currentValue.substring(0, currentValue.length - 1));
}
}
FormCard.FormCard {
id: darkThemeCard
maximumWidth: root.cardWidth
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
FormCard.FormSwitchDelegate {
id: darkThemeSwitch
text: i18n("Dark Theme")
checked: Prepare.PrepareUtil.usingDarkTheme
onCheckedChanged: {
if (checked !== Prepare.PrepareUtil.usingDarkTheme) {
Prepare.PrepareUtil.usingDarkTheme = checked;
}
} }
} }
} }
} }
} }
} }
} }

View file

@ -9,93 +9,97 @@ import org.kde.kirigami 2.20 as Kirigami
import org.kde.kirigamiaddons.formcard 1.0 as FormCard import org.kde.kirigamiaddons.formcard 1.0 as FormCard
import org.kde.plasma.mobileinitialstart.time 1.0 as Time import org.kde.plasma.mobileinitialstart.time 1.0 as Time
Item { import org.kde.plasma.mobileinitialstart.initialstart
id: root
property string name: i18n("Time and Date")
readonly property real cardWidth: Math.min(Kirigami.Units.gridUnit * 30, root.width - Kirigami.Units.gridUnit * 2) InitialStartModule {
contentItem: Item {
id: root
property string name: i18n("Time and Date")
ColumnLayout { readonly property real cardWidth: Math.min(Kirigami.Units.gridUnit * 30, root.width - Kirigami.Units.gridUnit * 2)
anchors {
fill: parent
topMargin: Kirigami.Units.gridUnit
bottomMargin: Kirigami.Units.largeSpacing
}
width: root.width ColumnLayout {
spacing: Kirigami.Units.gridUnit anchors {
fill: parent
Label { topMargin: Kirigami.Units.gridUnit
Layout.leftMargin: Kirigami.Units.gridUnit bottomMargin: Kirigami.Units.largeSpacing
Layout.rightMargin: Kirigami.Units.gridUnit
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
text: i18n("Select your time zone and preferred time format.")
}
FormCard.FormCard {
maximumWidth: root.cardWidth
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
Layout.fillWidth: true
FormCard.FormSwitchDelegate {
Layout.fillWidth: true
text: i18n("24-Hour Format")
checked: Time.TimeUtil.is24HourTime
onCheckedChanged: {
if (checked !== Time.TimeUtil.is24HourTime) {
Time.TimeUtil.is24HourTime = checked;
}
}
} }
}
FormCard.FormCard { width: root.width
maximumWidth: root.cardWidth spacing: Kirigami.Units.gridUnit
Layout.fillHeight: true Label {
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter Layout.leftMargin: Kirigami.Units.gridUnit
Layout.fillWidth: true Layout.rightMargin: Kirigami.Units.gridUnit
Layout.alignment: Qt.AlignTop
ListView {
id: listView
clip: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true
model: Time.TimeUtil.timeZones
currentIndex: -1 // ensure focus is not on the listview
header: Control { wrapMode: Text.Wrap
width: listView.width horizontalAlignment: Text.AlignHCenter
leftPadding: Kirigami.Units.largeSpacing text: i18n("Select your time zone and preferred time format.")
rightPadding: Kirigami.Units.largeSpacing }
topPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.largeSpacing
contentItem: Kirigami.SearchField { FormCard.FormCard {
id: searchField maximumWidth: root.cardWidth
onTextChanged: { Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
Time.TimeUtil.timeZones.filterString = text; Layout.fillWidth: true
FormCard.FormSwitchDelegate {
Layout.fillWidth: true
text: i18n("24-Hour Format")
checked: Time.TimeUtil.is24HourTime
onCheckedChanged: {
if (checked !== Time.TimeUtil.is24HourTime) {
Time.TimeUtil.is24HourTime = checked;
} }
} }
} }
}
delegate: FormCard.FormRadioDelegate { FormCard.FormCard {
required property string timeZoneId maximumWidth: root.cardWidth
width: ListView.view.width Layout.fillHeight: true
text: timeZoneId Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
checked: Time.TimeUtil.currentTimeZone === timeZoneId Layout.fillWidth: true
onCheckedChanged: {
if (checked && timeZoneId !== Time.TimeUtil.currentTimeZone) { ListView {
Time.TimeUtil.currentTimeZone = timeZoneId; id: listView
checked = Qt.binding(() => Time.TimeUtil.currentTimeZone === timeZoneId);
clip: true
Layout.fillWidth: true
Layout.fillHeight: true
model: Time.TimeUtil.timeZones
currentIndex: -1 // ensure focus is not on the listview
header: Control {
width: listView.width
leftPadding: Kirigami.Units.largeSpacing
rightPadding: Kirigami.Units.largeSpacing
topPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.largeSpacing
contentItem: Kirigami.SearchField {
id: searchField
onTextChanged: {
Time.TimeUtil.timeZones.filterString = text;
}
}
}
delegate: FormCard.FormRadioDelegate {
required property string timeZoneId
width: ListView.view.width
text: timeZoneId
checked: Time.TimeUtil.currentTimeZone === timeZoneId
onCheckedChanged: {
if (checked && timeZoneId !== Time.TimeUtil.currentTimeZone) {
Time.TimeUtil.currentTimeZone = timeZoneId;
checked = Qt.binding(() => Time.TimeUtil.currentTimeZone === timeZoneId);
}
} }
} }
} }
@ -103,4 +107,3 @@ Item {
} }
} }
} }

View file

@ -11,121 +11,123 @@ import org.kde.kirigamiaddons.formcard 1.0 as FormCard
import org.kde.plasma.networkmanagement as PlasmaNM import org.kde.plasma.networkmanagement as PlasmaNM
import org.kde.plasma.mobileinitialstart.wifi 1.0 as WiFi import org.kde.plasma.mobileinitialstart.wifi 1.0 as WiFi
Item { import org.kde.plasma.mobileinitialstart.initialstart
id: root
property string name: i18n("Network")
readonly property real cardWidth: Math.min(Kirigami.Units.gridUnit * 30, root.width - Kirigami.Units.gridUnit * 2) InitialStartModule {
contentItem: Item {
id: root
property string name: i18n("Network")
PlasmaNM.Handler { readonly property real cardWidth: Math.min(Kirigami.Units.gridUnit * 30, root.width - Kirigami.Units.gridUnit * 2)
id: handler
}
PlasmaNM.EnabledConnections { PlasmaNM.Handler {
id: enabledConnections id: handler
}
PlasmaNM.NetworkModel {
id: connectionModel
}
PlasmaNM.MobileProxyModel {
id: mobileProxyModel
sourceModel: connectionModel
showSavedMode: false
}
ConnectDialog {
id: connectionDialog
}
Component.onCompleted: handler.requestScan()
Timer {
id: scanTimer
interval: 10200
repeat: true
running: parent.visible
onTriggered: handler.requestScan()
}
ColumnLayout {
anchors {
fill: parent
topMargin: Kirigami.Units.gridUnit
bottomMargin: Kirigami.Units.largeSpacing
} }
width: root.width PlasmaNM.EnabledConnections {
spacing: Kirigami.Units.gridUnit id: enabledConnections
Label {
Layout.leftMargin: Kirigami.Units.gridUnit
Layout.rightMargin: Kirigami.Units.gridUnit
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
text: i18n("Connect to a WiFi network for network access.")
} }
FormCard.FormCard { PlasmaNM.NetworkModel {
id: savedCard id: connectionModel
maximumWidth: root.cardWidth }
visible: enabledConnections.wirelessEnabled && count > 0
// number of visible entries PlasmaNM.MobileProxyModel {
property int count: 0 id: mobileProxyModel
function updateCount() { sourceModel: connectionModel
count = 0; showSavedMode: false
for (let i = 0; i < connectedRepeater.count; i++) { }
let item = connectedRepeater.itemAt(i);
if (item && item.shouldDisplay) { ConnectDialog {
count++; id: connectionDialog
}
Component.onCompleted: handler.requestScan()
Timer {
id: scanTimer
interval: 10200
repeat: true
running: parent.visible
onTriggered: handler.requestScan()
}
ColumnLayout {
anchors {
fill: parent
topMargin: Kirigami.Units.gridUnit
bottomMargin: Kirigami.Units.largeSpacing
}
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
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
text: i18n("Connect to a WiFi network for network access.")
}
FormCard.FormCard {
id: savedCard
maximumWidth: root.cardWidth
visible: enabledConnections.wirelessEnabled && count > 0
// number of visible entries
property int count: 0
function updateCount() {
count = 0;
for (let i = 0; i < connectedRepeater.count; i++) {
let item = connectedRepeater.itemAt(i);
if (item && item.shouldDisplay) {
count++;
}
}
}
Repeater {
id: connectedRepeater
model: mobileProxyModel
delegate: ConnectionItemDelegate {
editMode: false
// connected or saved
property bool shouldDisplay: (Uuid != "") || ConnectionState === PlasmaNM.Enums.Activated
onShouldDisplayChanged: savedCard.updateCount()
// separate property for visible since visible is false when the whole card is not visible
visible: (Uuid != "") || ConnectionState === PlasmaNM.Enums.Activated
} }
} }
} }
Repeater { FormCard.FormCard {
id: connectedRepeater
model: mobileProxyModel
delegate: ConnectionItemDelegate {
editMode: false
// connected or saved
property bool shouldDisplay: (Uuid != "") || ConnectionState === PlasmaNM.Enums.Activated
onShouldDisplayChanged: savedCard.updateCount()
// separate property for visible since visible is false when the whole card is not visible
visible: (Uuid != "") || ConnectionState === PlasmaNM.Enums.Activated
}
}
}
FormCard.FormCard {
Layout.fillHeight: true
maximumWidth: root.cardWidth
visible: enabledConnections.wirelessEnabled
ListView {
id: listView
clip: true
Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
model: mobileProxyModel maximumWidth: root.cardWidth
visible: enabledConnections.wirelessEnabled
delegate: ConnectionItemDelegate { ListView {
width: ListView.view.width id: listView
editMode: false
height: visible ? implicitHeight : 0 clip: true
visible: !((Uuid != "") || ConnectionState === PlasmaNM.Enums.Activated) Layout.fillWidth: true
Layout.fillHeight: true
model: mobileProxyModel
delegate: ConnectionItemDelegate {
width: ListView.view.width
editMode: false
height: visible ? implicitHeight : 0
visible: !((Uuid != "") || ConnectionState === PlasmaNM.Enums.Activated)
}
} }
} }
} }
} }
} }

View file

@ -19,7 +19,7 @@ Kirigami.Page {
bottomPadding: 0 bottomPadding: 0
property int currentIndex: 0 property int currentIndex: 0
property int stepCount: 0 readonly property int stepCount: InitialStart.Wizard.stepsCount
property bool showingLanding: true property bool showingLanding: true
// filled by items // filled by items
@ -63,6 +63,11 @@ Kirigami.Page {
} }
} }
onStepCountChanged: {
// reset position
requestPreviousPage();
}
function finishFinalPage() { function finishFinalPage() {
// the app exits // the app exits
InitialStart.Wizard.wizardFinished(); InitialStart.Wizard.wizardFinished();
@ -207,7 +212,7 @@ Kirigami.Page {
delegate: MobileShell.BaseItem { delegate: MobileShell.BaseItem {
id: item id: item
visible: model.index === 0 // the binding is broken later visible: model.index === 0 // the binding is broken later
contentItem: modelData contentItem: modelData.contentItem
transform: Translate { transform: Translate {
x: { x: {
if (item.currentIndex === root.currentIndex - 1) { if (item.currentIndex === root.currentIndex - 1) {
@ -238,7 +243,6 @@ Kirigami.Page {
} }
Component.onCompleted: { Component.onCompleted: {
root.stepCount++
updateRootItems(); updateRootItems();
} }

View file

@ -50,12 +50,12 @@ void Wizard::load()
QQmlComponent *c = new QQmlComponent(m_engine, this); QQmlComponent *c = new QQmlComponent(m_engine, this);
// load initialstart QML items // load initialstart QML items
for (auto &pair : m_modulePackages) { for (const auto &[pluginMetadata, package] : m_modulePackages) {
// load QML from kpackage // load QML from kpackage
c->loadUrl(pair.second.fileUrl("mainscript"), QQmlComponent::PreferSynchronous); c->loadUrl(package.fileUrl("mainscript"), QQmlComponent::PreferSynchronous);
auto created = c->create(m_engine->rootContext()); auto created = c->create(m_engine->rootContext());
auto createdItem = qobject_cast<QQuickItem *>(created); InitialStartModule *createdItem = qobject_cast<InitialStartModule *>(created);
// print errors if there were issues loading // print errors if there were issues loading
if (!createdItem) { if (!createdItem) {
@ -67,12 +67,16 @@ void Wizard::load()
continue; continue;
} }
connect(createdItem, &InitialStartModule::availableChanged, this, &Wizard::determineAvailableModuleItems);
m_moduleItems.push_back(createdItem); m_moduleItems.push_back(createdItem);
qCDebug(LOGGING_CATEGORY) << "Loaded initialstart module" << pair.first->pluginId(); qCDebug(LOGGING_CATEGORY) << "Loaded initialstart module" << pluginMetadata->pluginId();
} }
delete c; delete c;
// Populate model
determineAvailableModuleItems();
} }
void Wizard::setTestingMode(bool testingMode) void Wizard::setTestingMode(bool testingMode)
@ -88,9 +92,14 @@ bool Wizard::testingMode()
return m_testingMode; return m_testingMode;
} }
QList<QQuickItem *> Wizard::steps() QList<InitialStartModule *> Wizard::steps()
{ {
return m_moduleItems; return m_availableModuleItems;
}
int Wizard::stepsCount()
{
return m_availableModuleItems.size();
} }
void Wizard::wizardFinished() void Wizard::wizardFinished()
@ -98,3 +107,15 @@ void Wizard::wizardFinished()
Settings::self()->setWizardFinished(); Settings::self()->setWizardFinished();
QCoreApplication::quit(); QCoreApplication::quit();
} }
void Wizard::determineAvailableModuleItems()
{
m_availableModuleItems.clear();
for (auto *moduleItem : m_moduleItems) {
if (moduleItem->available()) {
m_availableModuleItems.push_back(moduleItem);
}
}
Q_EMIT stepsChanged();
}

View file

@ -10,10 +10,13 @@
#include <KPackage/Package> #include <KPackage/Package>
#include <KPluginMetaData> #include <KPluginMetaData>
#include "initialstartmodule.h"
class Wizard : public QObject class Wizard : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QList<QQuickItem *> steps READ steps CONSTANT) Q_PROPERTY(QList<InitialStartModule *> steps READ steps NOTIFY stepsChanged)
Q_PROPERTY(int stepsCount READ stepsCount NOTIFY stepsChanged)
Q_PROPERTY(bool testingMode READ testingMode NOTIFY testingModeChanged) Q_PROPERTY(bool testingMode READ testingMode NOTIFY testingModeChanged)
public: public:
@ -24,17 +27,23 @@ public:
void setTestingMode(bool testingMode); void setTestingMode(bool testingMode);
bool testingMode(); bool testingMode();
QList<QQuickItem *> steps(); QList<InitialStartModule *> steps();
int stepsCount();
public Q_SLOTS: public Q_SLOTS:
void wizardFinished(); void wizardFinished();
Q_SIGNALS: Q_SIGNALS:
void stepsChanged();
void testingModeChanged(); void testingModeChanged();
private Q_SLOTS:
void determineAvailableModuleItems();
private: private:
QList<std::pair<KPluginMetaData *, KPackage::Package>> m_modulePackages; QList<std::pair<KPluginMetaData *, KPackage::Package>> m_modulePackages;
QList<QQuickItem *> m_moduleItems; QList<InitialStartModule *> m_availableModuleItems;
QList<InitialStartModule *> m_moduleItems;
bool m_testingMode; bool m_testingMode;
QQmlEngine *m_engine; QQmlEngine *m_engine;