Wallpaper grid in the settings app applying through the shell's wallpapers IPC. Lock pad now tracks the finger during the swipe and settles on release instead of snapping at a threshold.
226 lines
7.7 KiB
QML
226 lines
7.7 KiB
QML
//@ pragma UseQApplication
|
|
//@ pragma Env QS_NO_RELOAD_POPUP=1
|
|
//@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic
|
|
//@ pragma Env QT_QUICK_FLICKABLE_WHEEL_DECELERATION=10000
|
|
|
|
// Souveraine mobile settings — purpose-built for portrait/touch.
|
|
// Modeled on TouchLockSurface.qml: a full-screen surface with phone-native
|
|
// stack navigation (list -> push page -> back), not the desktop settings.qml's
|
|
// ApplicationWindow with titlebar + side rail. Reuses the existing
|
|
// ConfigSwitch/ConfigSpinBox pages (PhoneConfig.qml etc.) as the pushed
|
|
// content; only the chrome is mobile.
|
|
//
|
|
// Sized for 540x1080 logical (1080x2160 @ scale 2).
|
|
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
import QtQuick.Window
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import qs.services
|
|
import qs.modules.common
|
|
import qs.modules.common.widgets
|
|
import qs.modules.common.functions as CF
|
|
|
|
ApplicationWindow {
|
|
id: root
|
|
property int currentPage: -1 // -1 = list view; >=0 = drilled into that page
|
|
readonly property real rowHeight: 68 // finger-sized tap target
|
|
readonly property real edgeMargin: 16
|
|
|
|
// The page set. Same {name, icon, component} shape as the desktop
|
|
// settings.qml registry so a page is interchangeable between the two.
|
|
// One page per domain we own; About reuses ii's stock page verbatim.
|
|
property var pages: [
|
|
{
|
|
name: Translation.tr("Device"),
|
|
icon: "smartphone",
|
|
component: "modules/settings/DeviceConfig.qml"
|
|
},
|
|
{
|
|
name: Translation.tr("Lock screen"),
|
|
icon: "lock",
|
|
component: "modules/settings/LockConfig.qml"
|
|
},
|
|
{
|
|
name: Translation.tr("Wallpaper"),
|
|
icon: "wallpaper",
|
|
component: "modules/settings/WallpaperConfig.qml"
|
|
},
|
|
{
|
|
name: Translation.tr("Dock"),
|
|
icon: "dock_to_bottom",
|
|
component: "modules/settings/DockConfig.qml"
|
|
},
|
|
{
|
|
name: Translation.tr("Navigation"),
|
|
icon: "gesture",
|
|
component: "modules/settings/NavigationConfig.qml"
|
|
},
|
|
{
|
|
name: Translation.tr("Idle & sleep"),
|
|
icon: "bedtime",
|
|
component: "modules/settings/IdleConfig.qml"
|
|
},
|
|
{
|
|
name: Translation.tr("Keyboard"),
|
|
icon: "keyboard",
|
|
component: "modules/settings/KeyboardConfig.qml"
|
|
},
|
|
{
|
|
name: Translation.tr("About"),
|
|
icon: "info",
|
|
component: "modules/settings/About.qml"
|
|
}
|
|
// Next: Display (brightness/idle), Cellular (mmcli service),
|
|
// Bluetooth (after org.bluez activation is fixed), app health.
|
|
]
|
|
|
|
visible: true
|
|
onClosing: Qt.quit()
|
|
title: "Settings"
|
|
// Full-screen on the phone: no titlebar, no window-frame padding, edge to edge.
|
|
visibility: Window.FullScreen
|
|
color: Appearance.m3colors.m3background
|
|
|
|
Component.onCompleted: {
|
|
MaterialThemeLoader.reapplyTheme()
|
|
Config.readWriteDelay = 0
|
|
}
|
|
|
|
// --- List view (idle state) ------------------------------------------
|
|
ColumnLayout {
|
|
id: listView
|
|
anchors.fill: parent
|
|
spacing: 0
|
|
visible: root.currentPage < 0
|
|
|
|
// Header
|
|
Item {
|
|
Layout.fillWidth: true
|
|
Layout.leftMargin: root.edgeMargin
|
|
Layout.rightMargin: root.edgeMargin
|
|
Layout.topMargin: root.edgeMargin + 16
|
|
Layout.bottomMargin: 8
|
|
implicitHeight: titleText.implicitHeight
|
|
StyledText {
|
|
id: titleText
|
|
anchors.left: parent.left
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
color: Appearance.colors.colOnLayer0
|
|
text: Translation.tr("Settings")
|
|
font {
|
|
family: Appearance.font.family.title
|
|
pixelSize: Appearance.font.pixelSize.title
|
|
variableAxes: Appearance.font.variableAxes.title
|
|
}
|
|
}
|
|
}
|
|
|
|
// Full-width finger-sized rows
|
|
StyledListView {
|
|
id: pageList
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
model: root.pages
|
|
delegate: RippleButton {
|
|
required property var index
|
|
required property var modelData
|
|
width: pageList.width
|
|
height: root.rowHeight
|
|
buttonRadius: 0
|
|
onClicked: root.currentPage = index
|
|
contentItem: RowLayout {
|
|
anchors.fill: parent
|
|
anchors.leftMargin: root.edgeMargin
|
|
anchors.rightMargin: root.edgeMargin
|
|
spacing: 16
|
|
MaterialSymbol {
|
|
text: modelData.icon
|
|
iconSize: Appearance.font.pixelSize.larger
|
|
color: Appearance.colors.colOnLayer0
|
|
}
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
text: modelData.name
|
|
color: Appearance.colors.colOnLayer0
|
|
font.pixelSize: Appearance.font.pixelSize.larger
|
|
}
|
|
MaterialSymbol {
|
|
text: "chevron_right"
|
|
iconSize: Appearance.font.pixelSize.larger
|
|
color: Appearance.colors.colSubtext
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Page view (drilled-in state) ------------------------------------
|
|
Item {
|
|
id: pageView
|
|
anchors.fill: parent
|
|
visible: root.currentPage >= 0
|
|
|
|
// Back header
|
|
Item {
|
|
id: pageHeader
|
|
anchors {
|
|
top: parent.top
|
|
left: parent.left
|
|
right: parent.right
|
|
}
|
|
height: 56
|
|
RippleButton {
|
|
id: backButton
|
|
anchors {
|
|
left: parent.left
|
|
leftMargin: 4
|
|
verticalCenter: parent.verticalCenter
|
|
}
|
|
buttonRadius: Appearance.rounding.full
|
|
implicitWidth: 48
|
|
implicitHeight: 48
|
|
onClicked: root.currentPage = -1
|
|
contentItem: MaterialSymbol {
|
|
anchors.centerIn: parent
|
|
horizontalAlignment: Text.AlignHCenter
|
|
text: "arrow_back"
|
|
iconSize: 24
|
|
}
|
|
}
|
|
StyledText {
|
|
anchors {
|
|
left: backButton.right
|
|
leftMargin: 8
|
|
right: parent.right
|
|
rightMargin: root.edgeMargin
|
|
verticalCenter: parent.verticalCenter
|
|
}
|
|
color: Appearance.colors.colOnLayer0
|
|
text: root.currentPage >= 0 ? root.pages[root.currentPage].name : ""
|
|
elide: Text.ElideRight
|
|
font {
|
|
family: Appearance.font.family.title
|
|
pixelSize: Appearance.font.pixelSize.larger
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pushed page. ContentPage is a StyledFlickable that clamps to
|
|
// baseWidth 600 when forceWidth; we let it scroll and give it the
|
|
// full window width so it reads edge-to-edge.
|
|
Loader {
|
|
id: pageLoader
|
|
anchors {
|
|
top: pageHeader.bottom
|
|
left: parent.left
|
|
right: parent.right
|
|
bottom: parent.bottom
|
|
}
|
|
active: root.currentPage >= 0 && Config.ready
|
|
source: root.currentPage >= 0 ? root.pages[root.currentPage].component : ""
|
|
}
|
|
}
|
|
}
|