mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2025-12-06 06:36:15 +00:00
SetupWizard: initial commit
This commit is contained in:
@@ -1621,6 +1621,36 @@
|
||||
"missing-control-center": {
|
||||
"label": "Letztes Control-Center-Widget entfernt",
|
||||
"description": "Das Control-Center-Widget wurde aus der Leiste entfernt. Um es erneut über die Leiste zu öffnen, fügen Sie das Widget wieder hinzu. Sie können es auch durch Rechtsklick auf die Leiste öffnen."
|
||||
},
|
||||
"setup": {
|
||||
"customize": {
|
||||
"header": "Erlebnis anpassen",
|
||||
"subheader": "Leistenposition, Dichte, Skalierung und mehr einstellen."
|
||||
},
|
||||
"appearance": {
|
||||
"header": "Erscheinungsbild",
|
||||
"subheader": "Dunkelmodus und Farbquellen wählen (Matugen oder vordefiniert)."
|
||||
},
|
||||
"wallpaper": {
|
||||
"header": "Wähle dein Hintergrundbild",
|
||||
"subheader": "Bestimme die Stimmung mit einem schönen Hintergrund.",
|
||||
"select-prompt": "Wähle unten ein Hintergrundbild",
|
||||
"preview-error": "Bild konnte nicht geladen werden",
|
||||
"none-in-dir": "Keine Hintergrundbilder im Verzeichnis gefunden",
|
||||
"no-dir": "Kein Hintergrundbild-Verzeichnis ausgewählt",
|
||||
"no-valid": "Keine gültigen Bilddateien gefunden in: {dir}",
|
||||
"choose-dir": "Wähle ein Verzeichnis mit deinen Hintergrundbildern",
|
||||
"dir": {
|
||||
"label": "Hintergrundbild-Verzeichnis",
|
||||
"description": "Wähle den Ordner mit deinen Hintergrundbildern",
|
||||
"browse": "Ordner auswählen",
|
||||
"select-title": "Hintergrundbild-Ordner wählen"
|
||||
}
|
||||
}
|
||||
,
|
||||
"welcome": {
|
||||
"note": "Nur ein paar Grundeinstellungen – alle Optionen findest du in den Einstellungen"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1622,5 +1622,34 @@
|
||||
"lifespan": "Extended lifespan ({percent}%)",
|
||||
"disabled": "Battery manager disabled"
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"customize": {
|
||||
"header": "Customize your experience",
|
||||
"subheader": "Adjust bar position, density, scaling and more."
|
||||
},
|
||||
"appearance": {
|
||||
"header": "Appearance",
|
||||
"subheader": "Choose dark mode and color sources (Matugen or predefined)."
|
||||
},
|
||||
"wallpaper": {
|
||||
"header": "Choose your wallpaper",
|
||||
"subheader": "Set the mood with a beautiful background.",
|
||||
"select-prompt": "Select a wallpaper below",
|
||||
"preview-error": "Failed to load image",
|
||||
"none-in-dir": "No wallpapers found in directory",
|
||||
"no-dir": "No wallpaper directory selected",
|
||||
"no-valid": "No valid image files found in: {dir}",
|
||||
"choose-dir": "Choose a directory containing your wallpaper images",
|
||||
"dir": {
|
||||
"label": "Wallpaper directory",
|
||||
"description": "Choose the folder containing your wallpapers",
|
||||
"browse": "Browse for wallpaper folder",
|
||||
"select-title": "Select wallpaper folder"
|
||||
}
|
||||
},
|
||||
"welcome": {
|
||||
"note": "Just a few basics to get you started - full options are in Settings"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1622,5 +1622,35 @@
|
||||
"lifespan": "Vida útil prolongada ({percent}%)",
|
||||
"disabled": "Administrador de batería deshabilitado"
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"customize": {
|
||||
"header": "Personaliza tu experiencia",
|
||||
"subheader": "Ajusta la posición de la barra, densidad, escala y más."
|
||||
},
|
||||
"appearance": {
|
||||
"header": "Apariencia",
|
||||
"subheader": "Elige modo oscuro y fuentes de color (Matugen o predefinido)."
|
||||
},
|
||||
"wallpaper": {
|
||||
"header": "Elige tu fondo",
|
||||
"subheader": "Define el ambiente con un bonito fondo.",
|
||||
"select-prompt": "Selecciona un fondo abajo",
|
||||
"preview-error": "No se pudo cargar la imagen",
|
||||
"none-in-dir": "No se encontraron fondos en el directorio",
|
||||
"no-dir": "No se seleccionó un directorio de fondos",
|
||||
"no-valid": "No se encontraron imágenes válidas en: {dir}",
|
||||
"choose-dir": "Elige un directorio que contenga tus fondos",
|
||||
"dir": {
|
||||
"label": "Directorio de fondos",
|
||||
"description": "Elige la carpeta que contiene tus fondos",
|
||||
"browse": "Seleccionar carpeta",
|
||||
"select-title": "Seleccionar carpeta de fondos"
|
||||
}
|
||||
,
|
||||
"welcome": {
|
||||
"note": "Solo algunos ajustes básicos para empezar - el resto está en Configuración"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1623,4 +1623,35 @@
|
||||
"disabled": "Gestionnaire de batterie désactivé"
|
||||
}
|
||||
}
|
||||
,
|
||||
"setup": {
|
||||
"customize": {
|
||||
"header": "Personnaliser votre expérience",
|
||||
"subheader": "Ajustez la position de la barre, la densité, l'échelle et plus encore."
|
||||
},
|
||||
"appearance": {
|
||||
"header": "Apparence",
|
||||
"subheader": "Choisissez le mode sombre et la source des couleurs (Matugen ou prédéfinie)."
|
||||
},
|
||||
"wallpaper": {
|
||||
"header": "Choisissez votre fond d'écran",
|
||||
"subheader": "Définissez l'ambiance avec un joli fond.",
|
||||
"select-prompt": "Sélectionnez un fond ci-dessous",
|
||||
"preview-error": "Échec du chargement de l'image",
|
||||
"none-in-dir": "Aucun fond d'écran trouvé dans le répertoire",
|
||||
"no-dir": "Aucun répertoire de fonds d'écran sélectionné",
|
||||
"no-valid": "Aucun fichier image valide trouvé dans : {dir}",
|
||||
"choose-dir": "Choisissez un répertoire contenant vos fonds d'écran",
|
||||
"dir": {
|
||||
"label": "Répertoire des fonds d'écran",
|
||||
"description": "Choisissez le dossier contenant vos fonds d'écran",
|
||||
"browse": "Parcourir le dossier",
|
||||
"select-title": "Sélectionner le dossier des fonds d'écran"
|
||||
}
|
||||
}
|
||||
,
|
||||
"welcome": {
|
||||
"note": "Quelques réglages de base pour démarrer — toutes les options sont dans Paramètres"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1629,4 +1629,35 @@
|
||||
"uninstall-failed": "Falha na desinstalação"
|
||||
}
|
||||
}
|
||||
,
|
||||
"setup": {
|
||||
"customize": {
|
||||
"header": "Personalizar a sua experiência",
|
||||
"subheader": "Ajuste a posição da barra, densidade, escala e mais."
|
||||
},
|
||||
"appearance": {
|
||||
"header": "Aparência",
|
||||
"subheader": "Escolha o modo escuro e as fontes de cores (Matugen ou predefinidas)."
|
||||
},
|
||||
"wallpaper": {
|
||||
"header": "Escolha o seu papel de parede",
|
||||
"subheader": "Defina o ambiente com um belo fundo.",
|
||||
"select-prompt": "Selecione um papel de parede abaixo",
|
||||
"preview-error": "Falha ao carregar a imagem",
|
||||
"none-in-dir": "Nenhum papel de parede encontrado no diretório",
|
||||
"no-dir": "Nenhum diretório de papéis de parede selecionado",
|
||||
"no-valid": "Nenhuma imagem válida encontrada em: {dir}",
|
||||
"choose-dir": "Escolha um diretório contendo seus papéis de parede",
|
||||
"dir": {
|
||||
"label": "Diretório de papéis de parede",
|
||||
"description": "Escolha a pasta que contém seus papéis de parede",
|
||||
"browse": "Procurar pasta",
|
||||
"select-title": "Selecionar pasta de papéis de parede"
|
||||
}
|
||||
}
|
||||
,
|
||||
"welcome": {
|
||||
"note": "Apenas alguns ajustes básicos para começar - o restante está em Configurações"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1623,4 +1623,35 @@
|
||||
"disabled": "电池管理器已禁用"
|
||||
}
|
||||
}
|
||||
,
|
||||
"setup": {
|
||||
"customize": {
|
||||
"header": "自定义体验",
|
||||
"subheader": "调整状态栏位置、密度、缩放等。"
|
||||
},
|
||||
"appearance": {
|
||||
"header": "外观",
|
||||
"subheader": "选择深色模式与配色来源(Matugen 或预设)。"
|
||||
},
|
||||
"wallpaper": {
|
||||
"header": "选择你的壁纸",
|
||||
"subheader": "用精美壁纸营造氛围。",
|
||||
"select-prompt": "在下方选择一张壁纸",
|
||||
"preview-error": "图片加载失败",
|
||||
"none-in-dir": "目录中未找到壁纸",
|
||||
"no-dir": "未选择壁纸目录",
|
||||
"no-valid": "在 {dir} 中未找到有效图片文件",
|
||||
"choose-dir": "选择包含壁纸图片的目录",
|
||||
"dir": {
|
||||
"label": "壁纸目录",
|
||||
"description": "选择存放壁纸的文件夹",
|
||||
"browse": "浏览文件夹",
|
||||
"select-title": "选择壁纸文件夹"
|
||||
}
|
||||
}
|
||||
,
|
||||
"welcome": {
|
||||
"note": "先进行一些基础设置——更多选项可在“设置”中找到"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,8 @@
|
||||
"screenRadiusRatio": 1,
|
||||
"animationSpeed": 1,
|
||||
"animationDisabled": false,
|
||||
"compactLockScreen": false
|
||||
"compactLockScreen": false,
|
||||
"setupCompleted": false
|
||||
},
|
||||
"location": {
|
||||
"name": "Tokyo",
|
||||
|
||||
@@ -185,6 +185,7 @@ Singleton {
|
||||
property real animationSpeed: 1.0
|
||||
property bool animationDisabled: false
|
||||
property bool compactLockScreen: false
|
||||
property bool setupCompleted: false
|
||||
}
|
||||
|
||||
// location
|
||||
|
||||
555
Modules/SetupWizard/SetupAppearanceStep.qml
Normal file
555
Modules/SetupWizard/SetupAppearanceStep.qml
Normal file
@@ -0,0 +1,555 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: Style.marginM
|
||||
|
||||
function extractSchemeName(path) {
|
||||
var basename = path.split('/').pop()
|
||||
return basename.replace('.json', '')
|
||||
}
|
||||
|
||||
// Cache for scheme colors (mirrors ColorSchemeTab approach)
|
||||
property var schemeColorsCache: ({})
|
||||
property int cacheVersion: 0
|
||||
|
||||
function getSchemeColor(schemeName, key) {
|
||||
try {
|
||||
var mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
|
||||
var data = schemeColorsCache[schemeName]
|
||||
if (data && data[mode] && data[mode][key])
|
||||
return data[mode][key]
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
return Color.mSurfaceVariant
|
||||
}
|
||||
|
||||
// Match ColorSchemeTab helpers
|
||||
function schemeLoaded(schemeName, jsonData) {
|
||||
var value = jsonData || {}
|
||||
schemeColorsCache[schemeName] = value
|
||||
cacheVersion++
|
||||
Logger.log("SetupAppearanceStep", `Loaded scheme ${schemeName}`)
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ColorSchemeService
|
||||
function onSchemesChanged() {
|
||||
Logger.log("SetupAppearanceStep", `Color schemes changed: ${ColorSchemeService.schemes.length}`)
|
||||
schemeColorsCache = {}
|
||||
cacheVersion++
|
||||
}
|
||||
}
|
||||
|
||||
// Beautiful header with icon
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
|
||||
NIcon {
|
||||
icon: "palette"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginXS
|
||||
|
||||
NText {
|
||||
text: I18n.tr("setup.appearance.header")
|
||||
pointSize: Style.fontSizeXL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("setup.appearance.subheader")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
contentWidth: availableWidth
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: Style.marginM
|
||||
|
||||
// Dark Mode Toggle
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
|
||||
NIcon {
|
||||
icon: "moon"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.color-scheme.color-source.dark-mode.label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.color-scheme.color-source.dark-mode.description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
NToggle {
|
||||
checked: Settings.data.colorSchemes.darkMode
|
||||
onToggled: checked => Settings.data.colorSchemes.darkMode = checked
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Color.mOutline
|
||||
opacity: 0.2
|
||||
Layout.topMargin: Style.marginS
|
||||
Layout.bottomMargin: Style.marginS
|
||||
}
|
||||
|
||||
// Wallpaper Colors Toggle
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
|
||||
NIcon {
|
||||
icon: "color-picker"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.color-scheme.color-source.use-wallpaper-colors.label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.color-scheme.color-source.use-wallpaper-colors.description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
NToggle {
|
||||
enabled: ProgramCheckerService.matugenAvailable
|
||||
opacity: ProgramCheckerService.matugenAvailable ? 1.0 : 0.6
|
||||
checked: Settings.data.colorSchemes.useWallpaperColors && ProgramCheckerService.matugenAvailable
|
||||
onToggled: checked => {
|
||||
if (!ProgramCheckerService.matugenAvailable)
|
||||
return
|
||||
if (checked) {
|
||||
Settings.data.colorSchemes.useWallpaperColors = true
|
||||
AppThemeService.generate()
|
||||
} else {
|
||||
Settings.data.colorSchemes.useWallpaperColors = false
|
||||
if (Settings.data.colorSchemes.predefinedScheme) {
|
||||
ColorSchemeService.applyScheme(Settings.data.colorSchemes.predefinedScheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Matugen not available notice
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
visible: !ProgramCheckerService.matugenAvailable
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
NIcon {
|
||||
icon: "alert-triangle"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: I18n.tr("settings.color-scheme.color-source.use-wallpaper-colors.description")
|
||||
// Reuse description; availability is visually indicated
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
// Matugen scheme type (visible when wallpaper colors enabled and matugen available)
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
visible: Settings.data.colorSchemes.useWallpaperColors && ProgramCheckerService.matugenAvailable
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
|
||||
NIcon {
|
||||
icon: "wand"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.color-scheme.color-source.matugen-scheme-type.label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.color-scheme.color-source.matugen-scheme-type.description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Matugen scheme options styled like bar position buttons
|
||||
GridLayout {
|
||||
Layout.fillWidth: true
|
||||
columns: 2
|
||||
rowSpacing: Style.marginS
|
||||
columnSpacing: Style.marginS
|
||||
|
||||
Repeater {
|
||||
model: [{
|
||||
"key": "scheme-content",
|
||||
"name": "Content"
|
||||
}, {
|
||||
"key": "scheme-expressive",
|
||||
"name": "Expressive"
|
||||
}, {
|
||||
"key": "scheme-fidelity",
|
||||
"name": "Fidelity"
|
||||
}, {
|
||||
"key": "scheme-fruit-salad",
|
||||
"name": "Fruit Salad"
|
||||
}, {
|
||||
"key": "scheme-monochrome",
|
||||
"name": "Monochrome"
|
||||
}, {
|
||||
"key": "scheme-neutral",
|
||||
"name": "Neutral"
|
||||
}, {
|
||||
"key": "scheme-rainbow",
|
||||
"name": "Rainbow"
|
||||
}, {
|
||||
"key": "scheme-tonal-spot",
|
||||
"name": "Tonal Spot"
|
||||
}]
|
||||
delegate: Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 48
|
||||
radius: Style.radiusM
|
||||
border.width: 1
|
||||
|
||||
property bool isActive: Settings.data.colorSchemes.matugenSchemeType === modelData.key
|
||||
|
||||
color: (hoverHandler.hovered || isActive) ? Color.mPrimary : Color.mSurfaceVariant
|
||||
border.color: (hoverHandler.hovered || isActive) ? Color.mPrimary : Color.mOutline
|
||||
opacity: (hoverHandler.hovered || isActive) ? 1.0 : 0.8
|
||||
|
||||
NText {
|
||||
text: modelData.name
|
||||
pointSize: Style.fontSizeM
|
||||
font.weight: (hoverHandler.hovered || parent.isActive) ? Style.fontWeightBold : Style.fontWeightMedium
|
||||
color: (hoverHandler.hovered || parent.isActive) ? Color.mOnPrimary : Color.mOnSurface
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.data.colorSchemes.matugenSchemeType = modelData.key
|
||||
AppThemeService.generate()
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Color.mOutline
|
||||
opacity: 0.2
|
||||
Layout.topMargin: Style.marginS
|
||||
Layout.bottomMargin: Style.marginS
|
||||
visible: !Settings.data.colorSchemes.useWallpaperColors
|
||||
}
|
||||
|
||||
// Predefined schemes section (visible when wallpaper colors disabled)
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
visible: !Settings.data.colorSchemes.useWallpaperColors
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
|
||||
NIcon {
|
||||
icon: "palette"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.color-scheme.predefined.section.label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.color-scheme.predefined.section.description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Predefined schemes Grid (matches ColorSchemeTab)
|
||||
GridLayout {
|
||||
id: schemesGrid
|
||||
columns: Math.max(2, Math.floor((parent.width - Style.marginM * 2) / 180))
|
||||
rowSpacing: Style.marginM
|
||||
columnSpacing: Style.marginM
|
||||
Layout.fillWidth: true
|
||||
|
||||
Repeater {
|
||||
model: ColorSchemeService.schemes
|
||||
|
||||
delegate: Rectangle {
|
||||
id: schemeItem
|
||||
|
||||
property string schemePath: modelData
|
||||
property string schemeName: root.extractSchemeName(modelData)
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
height: 50
|
||||
radius: Style.radiusS
|
||||
color: (root.cacheVersionroot.getSchemeColor(schemeName, "mSurface"))
|
||||
border.width: Math.max(1, Style.borderL)
|
||||
border.color: itemMouseArea.containsMouse ? Color.mTertiary : (Settings.data.colorSchemes.predefinedScheme === schemeName ? Color.mSecondary : Color.mOutline)
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL
|
||||
spacing: Style.marginXS
|
||||
|
||||
NText {
|
||||
text: schemeItem.schemeName
|
||||
pointSize: Style.fontSizeS
|
||||
font.weight: Style.fontWeightMedium
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 1
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 14
|
||||
height: 14
|
||||
radius: width * 0.5
|
||||
color: (root.cacheVersionfunction () {
|
||||
var mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
|
||||
var cached = root.schemeColorsCache[schemeItem.schemeName]
|
||||
return (cached && cached[mode] && cached[mode].mPrimary) || root.getSchemeColor(schemeItem.schemeName, "mPrimary")
|
||||
})()
|
||||
}
|
||||
Rectangle {
|
||||
width: 14
|
||||
height: 14
|
||||
radius: width * 0.5
|
||||
color: (root.cacheVersionfunction () {
|
||||
var mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
|
||||
var cached = root.schemeColorsCache[schemeItem.schemeName]
|
||||
return (cached && cached[mode] && cached[mode].mSecondary) || root.getSchemeColor(schemeItem.schemeName, "mSecondary")
|
||||
})()
|
||||
}
|
||||
Rectangle {
|
||||
width: 14
|
||||
height: 14
|
||||
radius: width * 0.5
|
||||
color: (root.cacheVersionfunction () {
|
||||
var mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
|
||||
var cached = root.schemeColorsCache[schemeItem.schemeName]
|
||||
return (cached && cached[mode] && cached[mode].mTertiary) || root.getSchemeColor(schemeItem.schemeName, "mTertiary")
|
||||
})()
|
||||
}
|
||||
Rectangle {
|
||||
width: 14
|
||||
height: 14
|
||||
radius: width * 0.5
|
||||
color: (root.cacheVersionfunction () {
|
||||
var mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
|
||||
var cached = root.schemeColorsCache[schemeItem.schemeName]
|
||||
return (cached && cached[mode] && cached[mode].mError) || root.getSchemeColor(schemeItem.schemeName, "mError")
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: itemMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.data.colorSchemes.useWallpaperColors = false
|
||||
Settings.data.colorSchemes.predefinedScheme = schemeItem.schemeName
|
||||
ColorSchemeService.applyScheme(Settings.data.colorSchemes.predefinedScheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom spacer
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.marginL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hidden loader to populate schemeColorsCache from files
|
||||
Item {
|
||||
visible: false
|
||||
Repeater {
|
||||
model: ColorSchemeService.schemes
|
||||
delegate: Item {
|
||||
FileView {
|
||||
path: modelData
|
||||
blockLoading: false
|
||||
onLoaded: {
|
||||
var schemeName = root.extractSchemeName(path)
|
||||
try {
|
||||
var jsonData = JSON.parse(text())
|
||||
root.schemeLoaded(schemeName, jsonData)
|
||||
} catch (e) {
|
||||
root.schemeLoaded(schemeName, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
522
Modules/SetupWizard/SetupCustomizeStep.qml
Normal file
522
Modules/SetupWizard/SetupCustomizeStep.qml
Normal file
@@ -0,0 +1,522 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property real selectedScaleRatio: 1.0
|
||||
property string selectedBarPosition: "top"
|
||||
property bool selectedDimDesktop: true
|
||||
|
||||
signal scaleRatioChanged(real ratio)
|
||||
signal barPositionChanged(string position)
|
||||
signal dimDesktopChanged(bool dim)
|
||||
|
||||
spacing: Style.marginM
|
||||
|
||||
// Beautiful header with icon
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
|
||||
NIcon {
|
||||
icon: "palette"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginXS
|
||||
|
||||
NText {
|
||||
text: I18n.tr("setup.customize.header")
|
||||
pointSize: Style.fontSizeXL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("setup.customize.subheader")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
contentWidth: availableWidth
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: Style.marginM
|
||||
|
||||
// Bar Position section
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
|
||||
NIcon {
|
||||
icon: "layout-2"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.bar.appearance.position.label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.bar.appearance.position.description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
Layout.fillWidth: true
|
||||
columns: 2
|
||||
rowSpacing: Style.marginS
|
||||
columnSpacing: Style.marginS
|
||||
|
||||
Repeater {
|
||||
model: [{
|
||||
"key": "top",
|
||||
"name": I18n.tr("options.bar.position.top"),
|
||||
"icon": "arrow-up"
|
||||
}, {
|
||||
"key": "bottom",
|
||||
"name": I18n.tr("options.bar.position.bottom"),
|
||||
"icon": "arrow-down"
|
||||
}, {
|
||||
"key": "left",
|
||||
"name": I18n.tr("options.bar.position.left"),
|
||||
"icon": "arrow-left"
|
||||
}, {
|
||||
"key": "right",
|
||||
"name": I18n.tr("options.bar.position.right"),
|
||||
"icon": "arrow-right"
|
||||
}]
|
||||
delegate: Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 48
|
||||
radius: Style.radiusM
|
||||
border.width: 1
|
||||
|
||||
property bool isActive: selectedBarPosition === modelData.key
|
||||
|
||||
color: (hoverHandler.hovered || isActive) ? Color.mPrimary : Color.mSurfaceVariant
|
||||
border.color: (hoverHandler.hovered || isActive) ? Color.mPrimary : Color.mOutline
|
||||
opacity: (hoverHandler.hovered || isActive) ? 1.0 : 0.8
|
||||
|
||||
NText {
|
||||
text: modelData.name
|
||||
pointSize: Style.fontSizeM
|
||||
font.weight: (hoverHandler.hovered || parent.isActive) ? Style.fontWeightBold : Style.fontWeightMedium
|
||||
color: (hoverHandler.hovered || parent.isActive) ? Color.mOnPrimary : Color.mOnSurface
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
selectedBarPosition = modelData.key
|
||||
barPositionChanged(modelData.key)
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Color.mOutline
|
||||
opacity: 0.2
|
||||
Layout.topMargin: Style.marginS
|
||||
Layout.bottomMargin: Style.marginS
|
||||
}
|
||||
|
||||
// Dim Desktop section
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
Rectangle {
|
||||
width: 32
|
||||
height: 32
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
NIcon {
|
||||
icon: "moon"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
NText {
|
||||
text: I18n.tr("settings.user-interface.dim-desktop.label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
NText {
|
||||
text: I18n.tr("settings.user-interface.dim-desktop.description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
NToggle {
|
||||
checked: selectedDimDesktop
|
||||
onToggled: function (checked) {
|
||||
selectedDimDesktop = checked
|
||||
dimDesktopChanged(checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Color.mOutline
|
||||
opacity: 0.2
|
||||
Layout.topMargin: Style.marginS
|
||||
Layout.bottomMargin: Style.marginS
|
||||
}
|
||||
|
||||
// Bar Density section
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
Rectangle {
|
||||
width: 32
|
||||
height: 32
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
NIcon {
|
||||
icon: "minimize"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
NText {
|
||||
text: I18n.tr("settings.bar.appearance.density.label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
NText {
|
||||
text: I18n.tr("settings.bar.appearance.density.description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.marginS
|
||||
Repeater {
|
||||
model: [{
|
||||
"key": "mini",
|
||||
"name": I18n.tr("options.bar.density.mini")
|
||||
}, {
|
||||
"key": "compact",
|
||||
"name": I18n.tr("options.bar.density.compact")
|
||||
}, {
|
||||
"key": "default",
|
||||
"name": I18n.tr("options.bar.density.default")
|
||||
}, {
|
||||
"key": "comfortable",
|
||||
"name": I18n.tr("options.bar.density.comfortable")
|
||||
}]
|
||||
delegate: Rectangle {
|
||||
radius: 16
|
||||
border.width: 1
|
||||
Layout.preferredHeight: 32
|
||||
Layout.preferredWidth: Math.max(72, densityText.implicitWidth + Style.marginM)
|
||||
|
||||
property bool isActive: Settings.data.bar.density === modelData.key
|
||||
|
||||
color: (hoverHandler.hovered || isActive) ? Color.mPrimary : Color.mSurfaceVariant
|
||||
border.color: (hoverHandler.hovered || isActive) ? Color.mPrimary : Color.mOutline
|
||||
opacity: (hoverHandler.hovered || isActive) ? 1.0 : 0.8
|
||||
|
||||
NText {
|
||||
id: densityText
|
||||
text: modelData.name
|
||||
pointSize: Style.fontSizeS
|
||||
font.weight: (hoverHandler.hovered || parent.isActive) ? Style.fontWeightBold : Style.fontWeightMedium
|
||||
color: (hoverHandler.hovered || parent.isActive) ? Color.mOnPrimary : Color.mOnSurface
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.data.bar.density = modelData.key
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Color.mOutline
|
||||
opacity: 0.2
|
||||
Layout.topMargin: Style.marginS
|
||||
Layout.bottomMargin: Style.marginS
|
||||
}
|
||||
|
||||
// UI Scale section
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
Rectangle {
|
||||
width: 32
|
||||
height: 32
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
NIcon {
|
||||
icon: "maximize"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
NText {
|
||||
text: I18n.tr("settings.user-interface.scaling.label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
NText {
|
||||
text: I18n.tr("settings.user-interface.scaling.description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
NText {
|
||||
text: "80%"
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
from: 0.8
|
||||
to: 1.2
|
||||
stepSize: 0.05
|
||||
value: selectedScaleRatio
|
||||
onMoved: function (value) {
|
||||
selectedScaleRatio = value
|
||||
scaleRatioChanged(value)
|
||||
}
|
||||
text: Math.floor(selectedScaleRatio * 100) + "%"
|
||||
}
|
||||
NText {
|
||||
text: "120%"
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Color.mOutline
|
||||
opacity: 0.2
|
||||
Layout.topMargin: Style.marginS
|
||||
Layout.bottomMargin: Style.marginS
|
||||
}
|
||||
|
||||
// Bar Floating toggle
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
Rectangle {
|
||||
width: 32
|
||||
height: 32
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
NIcon {
|
||||
icon: "layout-2"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
NText {
|
||||
text: I18n.tr("settings.bar.appearance.floating.label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
NText {
|
||||
text: I18n.tr("settings.bar.appearance.floating.description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
NToggle {
|
||||
checked: Settings.data.bar.floating
|
||||
onToggled: function (checked) {
|
||||
Settings.data.bar.floating = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Color.mOutline
|
||||
opacity: 0.2
|
||||
Layout.topMargin: Style.marginS
|
||||
Layout.bottomMargin: Style.marginS
|
||||
}
|
||||
|
||||
// Bar Background Opacity
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
NLabel {
|
||||
label: I18n.tr("settings.bar.appearance.background-opacity.label")
|
||||
description: I18n.tr("settings.bar.appearance.background-opacity.description")
|
||||
}
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 1
|
||||
stepSize: 0.01
|
||||
value: Settings.data.bar.backgroundOpacity
|
||||
onMoved: function (value) {
|
||||
Settings.data.bar.backgroundOpacity = value
|
||||
}
|
||||
text: Math.floor(Settings.data.bar.backgroundOpacity * 100) + "%"
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.marginL
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
507
Modules/SetupWizard/SetupWallpaperStep.qml
Normal file
507
Modules/SetupWizard/SetupWallpaperStep.qml
Normal file
@@ -0,0 +1,507 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import "../../Helpers/FuzzySort.js" as FuzzySort
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property string selectedDirectory: ""
|
||||
property string selectedWallpaper: ""
|
||||
|
||||
signal directoryChanged(string directory)
|
||||
signal wallpaperChanged(string wallpaper)
|
||||
|
||||
spacing: Style.marginL
|
||||
|
||||
// Beautiful header with icon
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.marginM
|
||||
|
||||
Rectangle {
|
||||
width: 40
|
||||
height: 40
|
||||
radius: Style.radiusL
|
||||
color: Color.mSurfaceVariant
|
||||
opacity: 0.6
|
||||
|
||||
NIcon {
|
||||
icon: "image"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Style.marginXS
|
||||
|
||||
NText {
|
||||
text: I18n.tr("setup.wallpaper.header")
|
||||
pointSize: Style.fontSizeXL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("setup.wallpaper.subheader")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Large preview with rounded corners and shadow effect
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumHeight: 180
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusL
|
||||
border.color: selectedWallpaper !== "" ? Color.mPrimary : Color.mOutline
|
||||
border.width: selectedWallpaper !== "" ? 2 : 1
|
||||
clip: true
|
||||
|
||||
// Mirror WallpaperPanel approach with rounded shader mask
|
||||
NImageCached {
|
||||
id: previewCached
|
||||
anchors.fill: parent
|
||||
anchors.margins: 4
|
||||
maxCacheDimension: 512
|
||||
cacheFolder: Settings.cacheDirImagesWallpapers
|
||||
imagePath: selectedWallpaper !== "" ? "file://" + selectedWallpaper : ""
|
||||
visible: false // used as texture source for the shader
|
||||
}
|
||||
|
||||
ShaderEffect {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 4
|
||||
property var source: ShaderEffectSource {
|
||||
sourceItem: previewCached
|
||||
hideSource: true
|
||||
live: true
|
||||
recursive: false
|
||||
format: ShaderEffectSource.RGBA
|
||||
}
|
||||
property real itemWidth: width
|
||||
property real itemHeight: height
|
||||
property real cornerRadius: Style.radiusL
|
||||
property real imageOpacity: 1.0
|
||||
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/rounded_image.frag.qsb")
|
||||
supportsAtlasTextures: false
|
||||
blending: true
|
||||
}
|
||||
|
||||
// Loading placeholder
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusL
|
||||
visible: (previewCached.status === Image.Loading || previewCached.status === Image.Null) && selectedWallpaper !== ""
|
||||
|
||||
NIcon {
|
||||
icon: "image"
|
||||
pointSize: Style.fontSizeXXL
|
||||
color: Color.mOnSurfaceVariant
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
// Error placeholder
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.mError
|
||||
opacity: 0.1
|
||||
radius: Style.radiusL
|
||||
visible: previewCached.status === Image.Error && selectedWallpaper !== ""
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: Style.marginS
|
||||
|
||||
NIcon {
|
||||
icon: "alert-circle"
|
||||
pointSize: Style.fontSizeXXL
|
||||
color: Color.mError
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("setup.wallpaper.preview-error")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mError
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NBusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
visible: (previewCached.status === Image.Loading || previewCached.status === Image.Null) && selectedWallpaper !== ""
|
||||
running: visible
|
||||
size: 28
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: Style.marginL
|
||||
visible: selectedWallpaper === ""
|
||||
opacity: 0.6
|
||||
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
width: 64
|
||||
height: 64
|
||||
radius: width / 2
|
||||
color: Color.mPrimary
|
||||
opacity: 0.15
|
||||
|
||||
NIcon {
|
||||
icon: "sparkles"
|
||||
pointSize: Style.fontSizeXXL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("setup.wallpaper.select-prompt")
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.weight: Style.fontWeightMedium
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wallpaper gallery strip
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 90
|
||||
visible: filteredWallpapers.length > 0
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
ScrollBar.horizontal.policy: ScrollBar.AsNeeded
|
||||
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.marginM
|
||||
height: parent.height
|
||||
|
||||
Repeater {
|
||||
model: filteredWallpapers
|
||||
delegate: Rectangle {
|
||||
Layout.preferredWidth: 120
|
||||
Layout.preferredHeight: 80
|
||||
color: Color.mSurface
|
||||
radius: Style.radiusM
|
||||
border.color: selectedWallpaper === modelData ? Color.mPrimary : Color.mOutline
|
||||
border.width: selectedWallpaper === modelData ? 2 : 1
|
||||
clip: true
|
||||
|
||||
// Cached thumbnail (used as shader source)
|
||||
NImageCached {
|
||||
id: thumbCached
|
||||
anchors.fill: parent
|
||||
anchors.margins: 3
|
||||
maxCacheDimension: 256
|
||||
cacheFolder: Settings.cacheDirImagesWallpapers
|
||||
imagePath: "file://" + modelData
|
||||
visible: false
|
||||
}
|
||||
|
||||
ShaderEffect {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 3
|
||||
property var source: ShaderEffectSource {
|
||||
sourceItem: thumbCached
|
||||
hideSource: true
|
||||
live: true
|
||||
recursive: false
|
||||
format: ShaderEffectSource.RGBA
|
||||
}
|
||||
property real itemWidth: width
|
||||
property real itemHeight: height
|
||||
property real cornerRadius: Style.radiusM - 3
|
||||
property real imageOpacity: 1.0
|
||||
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/rounded_image.frag.qsb")
|
||||
supportsAtlasTextures: false
|
||||
blending: true
|
||||
}
|
||||
|
||||
// Loading state
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusM
|
||||
visible: thumbCached.status === Image.Loading
|
||||
|
||||
NIcon {
|
||||
icon: "image"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mOnSurfaceVariant
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
// Error state
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusM
|
||||
visible: thumbCached.status === Image.Error
|
||||
|
||||
NIcon {
|
||||
icon: "image"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mOnSurfaceVariant
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
NBusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
visible: thumbCached.status === Image.Loading || thumbCached.status === Image.Null
|
||||
running: visible
|
||||
size: 18
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.mPrimary
|
||||
opacity: hoverHandler.hovered ? 0.1 : 0
|
||||
radius: Style.radiusM
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: selectedWallpaper === modelData
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: 6
|
||||
width: 24
|
||||
height: 24
|
||||
radius: width / 2
|
||||
color: Color.mPrimary
|
||||
|
||||
NIcon {
|
||||
icon: "check"
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onTapped: {
|
||||
selectedWallpaper = modelData
|
||||
wallpaperChanged(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helpful info card
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 80
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusM
|
||||
opacity: 0.4
|
||||
visible: filteredWallpapers.length === 0
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL
|
||||
spacing: Style.marginM
|
||||
|
||||
NIcon {
|
||||
icon: "folder-open"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginXS
|
||||
NText {
|
||||
text: filteredWallpapers.length === 0 && selectedDirectory !== "" ? I18n.tr("setup.wallpaper.none-in-dir") : I18n.tr("setup.wallpaper.no-dir")
|
||||
pointSize: Style.fontSizeM
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
NText {
|
||||
text: selectedDirectory !== "" ? I18n.tr("setup.wallpaper.no-valid", {
|
||||
"dir": selectedDirectory
|
||||
}) : I18n.tr("setup.wallpaper.choose-dir")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
opacity: 0.8
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Directory selection
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
NTextInputButton {
|
||||
id: wallpaperPathInput
|
||||
label: I18n.tr("setup.wallpaper.dir.label")
|
||||
description: I18n.tr("setup.wallpaper.dir.description")
|
||||
text: selectedDirectory
|
||||
buttonIcon: "folder-open"
|
||||
buttonTooltip: I18n.tr("setup.wallpaper.dir.browse")
|
||||
Layout.fillWidth: true
|
||||
onInputEditingFinished: {
|
||||
selectedDirectory = text
|
||||
directoryChanged(text)
|
||||
}
|
||||
onButtonClicked: directoryPicker.open()
|
||||
}
|
||||
}
|
||||
|
||||
// Internal properties and functions
|
||||
property list<string> wallpapersList: []
|
||||
property list<string> filteredWallpapers: []
|
||||
|
||||
function updateFilteredWallpapers() {
|
||||
filteredWallpapers = wallpapersList
|
||||
}
|
||||
|
||||
function refreshWallpapers() {
|
||||
if (!selectedDirectory || selectedDirectory === "") {
|
||||
wallpapersList = []
|
||||
filteredWallpapers = []
|
||||
return
|
||||
}
|
||||
if (typeof WallpaperService !== "undefined" && WallpaperService.getWallpapersList) {
|
||||
var wallpapers = WallpaperService.getWallpapersList(Screen.name)
|
||||
wallpapersList = wallpapers
|
||||
updateFilteredWallpapers()
|
||||
if (wallpapersList.length > 0 && selectedWallpaper === "") {
|
||||
selectedWallpaper = wallpapersList[0]
|
||||
}
|
||||
} else {
|
||||
readDirectoryImages(selectedDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
function readDirectoryImages(directoryPath) {
|
||||
directoryScanner.command = ["find", directoryPath, "-type", "f", "\\(-iname", "*.jpg", "-o", "-iname", "*.jpeg", "-o", "-iname", "*.png", "-o", "-iname", "*.bmp", "-o", "-iname", "*.webp", "-o", "-iname", "*.svg", "\\)"]
|
||||
directoryScanner.running = true
|
||||
return []
|
||||
}
|
||||
|
||||
onSelectedDirectoryChanged: {
|
||||
if (typeof Settings !== "undefined" && Settings.data && Settings.data.wallpaper) {
|
||||
Settings.data.wallpaper.directory = selectedDirectory
|
||||
}
|
||||
if (typeof WallpaperService !== "undefined" && WallpaperService.refreshWallpapersList) {
|
||||
WallpaperService.refreshWallpapersList()
|
||||
}
|
||||
Qt.callLater(refreshWallpapers)
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: WallpaperService
|
||||
enabled: typeof WallpaperService !== "undefined"
|
||||
function onWallpaperListChanged(screenName, count) {
|
||||
if (screenName === Screen.name) {
|
||||
Qt.callLater(refreshWallpapers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: initialRefreshTimer
|
||||
interval: 1000
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: refreshWallpapers()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (typeof Settings !== "undefined" && Settings.data && Settings.data.wallpaper && Settings.data.wallpaper.directory) {
|
||||
selectedDirectory = Settings.data.wallpaper.directory
|
||||
} else {
|
||||
selectedDirectory = Quickshell.env("HOME") + "/Pictures/Wallpapers"
|
||||
}
|
||||
if (typeof WallpaperService !== "undefined" && WallpaperService.currentWallpaper) {
|
||||
selectedWallpaper = WallpaperService.currentWallpaper
|
||||
}
|
||||
initialRefreshTimer.start()
|
||||
}
|
||||
|
||||
NFilePicker {
|
||||
id: directoryPicker
|
||||
selectionMode: "folders"
|
||||
title: I18n.tr("setup.wallpaper.dir.select-title")
|
||||
initialPath: selectedDirectory || Quickshell.env("HOME") + "/Pictures"
|
||||
onAccepted: paths => {
|
||||
if (paths.length > 0) {
|
||||
selectedDirectory = paths[0]
|
||||
directoryChanged(paths[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: directoryScanner
|
||||
command: ["find", "", "-type", "f", "\\(-iname", "*.jpg", "-o", "-iname", "*.jpeg", "-o", "-iname", "*.png", "-o", "-iname", "*.bmp", "-o", "-iname", "*.webp", "-o", "-iname", "*.svg", "\\)"]
|
||||
running: false
|
||||
stdout: StdioCollector {}
|
||||
stderr: StdioCollector {}
|
||||
onExited: function (exitCode) {
|
||||
if (exitCode === 0) {
|
||||
var lines = stdout.text.split('\n')
|
||||
var images = []
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i].trim()
|
||||
if (line !== '') {
|
||||
images.push(line)
|
||||
}
|
||||
}
|
||||
wallpapersList = images
|
||||
updateFilteredWallpapers()
|
||||
if (wallpapersList.length > 0 && selectedWallpaper === "") {
|
||||
selectedWallpaper = wallpapersList[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
414
Modules/SetupWizard/SetupWizard.qml
Normal file
414
Modules/SetupWizard/SetupWizard.qml
Normal file
@@ -0,0 +1,414 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
NPanel {
|
||||
id: root
|
||||
|
||||
preferredWidth: 520
|
||||
preferredHeight: 600
|
||||
preferredWidthRatio: 0.4
|
||||
preferredHeightRatio: 0.6
|
||||
panelAnchorHorizontalCenter: true
|
||||
panelAnchorVerticalCenter: true
|
||||
panelKeyboardFocus: true
|
||||
|
||||
// Prevent closing during setup
|
||||
backgroundClickEnabled: false
|
||||
draggable: false
|
||||
|
||||
property int currentStep: 0
|
||||
property int totalSteps: 4
|
||||
|
||||
// Setup wizard data
|
||||
property string selectedWallpaperDirectory: Settings.defaultWallpapersDirectory
|
||||
property string selectedWallpaper: ""
|
||||
property real selectedScaleRatio: 1.0
|
||||
property string selectedBarPosition: "top"
|
||||
property bool selectedDimDesktop: true
|
||||
|
||||
panelContent: Component {
|
||||
Item {
|
||||
id: container
|
||||
anchors.fill: parent
|
||||
|
||||
ColumnLayout {
|
||||
id: wizardContent
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginXL
|
||||
spacing: Style.marginL
|
||||
|
||||
// Override ESC key to prevent closing during setup
|
||||
Shortcut {
|
||||
sequences: ["Escape"]
|
||||
enabled: root.active
|
||||
onActivated: {
|
||||
|
||||
// Do nothing - prevent ESC from closing the setup wizard
|
||||
}
|
||||
context: Qt.WindowShortcut
|
||||
}
|
||||
|
||||
// Step content - takes most of the space
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumHeight: 300
|
||||
|
||||
StackLayout {
|
||||
id: stepStack
|
||||
anchors.fill: parent
|
||||
currentIndex: currentStep
|
||||
|
||||
// Step 0: Welcome - Beautiful centered design
|
||||
Item {
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
width: Math.min(parent.width - Style.marginXL * 2, 420)
|
||||
spacing: Style.marginXL
|
||||
|
||||
// Logo with subtle glow effect
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 120
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: 120
|
||||
height: 120
|
||||
radius: width / 2
|
||||
color: Color.mPrimary
|
||||
opacity: 0.08
|
||||
scale: 1.3
|
||||
}
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 110
|
||||
height: 110
|
||||
source: "https://assets.noctalia.dev/noctalia-logo.svg"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
smooth: true
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.mSurfaceVariant
|
||||
radius: width / 2
|
||||
border.color: Color.mOutline
|
||||
border.width: 2
|
||||
visible: parent.status === Image.Error
|
||||
|
||||
NIcon {
|
||||
icon: "sparkles"
|
||||
pointSize: Style.fontSizeXXL * 1.5
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
// Subtle pulse animation
|
||||
SequentialAnimation on scale {
|
||||
running: true
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation {
|
||||
from: 1.0
|
||||
to: 1.05
|
||||
duration: 2000
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
from: 1.05
|
||||
to: 1.0
|
||||
duration: 2000
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Welcome text with gradient feel
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Style.marginM
|
||||
|
||||
NText {
|
||||
text: "Welcome to Noctalia! ✨"
|
||||
pointSize: Style.fontSizeXXL * 1.4
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Let's make your desktop uniquely yours"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
// Friendly subtext
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginL
|
||||
Layout.preferredHeight: childrenRect.height + Style.marginM * 2
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusL
|
||||
opacity: 0.4
|
||||
|
||||
NText {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Style.marginL * 2
|
||||
text: I18n.tr("setup.welcome.note")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurfaceVariant
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1: Wallpaper Setup
|
||||
SetupWallpaperStep {
|
||||
id: step1
|
||||
selectedDirectory: root.selectedWallpaperDirectory
|
||||
selectedWallpaper: root.selectedWallpaper
|
||||
onDirectoryChanged: function (directory) {
|
||||
root.selectedWallpaperDirectory = directory
|
||||
root.applyWallpaperSettings()
|
||||
}
|
||||
onWallpaperChanged: function (wallpaper) {
|
||||
root.selectedWallpaper = wallpaper
|
||||
root.applyWallpaperSettings()
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: UI Configuration
|
||||
SetupCustomizeStep {
|
||||
id: step2
|
||||
selectedScaleRatio: root.selectedScaleRatio
|
||||
selectedBarPosition: root.selectedBarPosition
|
||||
selectedDimDesktop: root.selectedDimDesktop
|
||||
onScaleRatioChanged: function (ratio) {
|
||||
root.selectedScaleRatio = ratio
|
||||
root.applyUISettings()
|
||||
}
|
||||
onBarPositionChanged: function (position) {
|
||||
root.selectedBarPosition = position
|
||||
root.applyUISettings()
|
||||
}
|
||||
onDimDesktopChanged: function (dim) {
|
||||
root.selectedDimDesktop = dim
|
||||
root.applyUISettings()
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Appearance - Dark mode and color source
|
||||
SetupAppearanceStep {
|
||||
id: step3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Elegant divider
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Color.mOutline
|
||||
opacity: 0.2
|
||||
}
|
||||
|
||||
// Modern progress indicator with labels
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 32
|
||||
|
||||
RowLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: Style.marginM
|
||||
|
||||
Repeater {
|
||||
model: [{
|
||||
"icon": "sparkles",
|
||||
"label": "Welcome"
|
||||
}, {
|
||||
"icon": "image",
|
||||
"label": "Wallpaper"
|
||||
}, {
|
||||
"icon": "settings",
|
||||
"label": "Customize"
|
||||
}, {
|
||||
"icon": "palette",
|
||||
"label": "Appearance"
|
||||
}]
|
||||
delegate: RowLayout {
|
||||
spacing: Style.marginS
|
||||
|
||||
Rectangle {
|
||||
width: 24
|
||||
height: 24
|
||||
radius: width / 2
|
||||
color: index <= currentStep ? Color.mPrimary : Color.mSurfaceVariant
|
||||
border.color: index === currentStep ? Color.mPrimary : "transparent"
|
||||
border.width: index === currentStep ? 2 : 0
|
||||
|
||||
NIcon {
|
||||
icon: modelData.icon
|
||||
pointSize: Style.fontSizeS
|
||||
color: index <= currentStep ? Color.mOnPrimary : Color.mOnSurfaceVariant
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: modelData.label
|
||||
pointSize: Style.fontSizeS
|
||||
color: index <= currentStep ? Color.mPrimary : Color.mOnSurfaceVariant
|
||||
font.weight: index === currentStep ? Style.fontWeightBold : Style.fontWeightRegular
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Connector line
|
||||
Rectangle {
|
||||
width: 40
|
||||
height: 2
|
||||
radius: 1
|
||||
color: index < currentStep ? Color.mPrimary : Color.mSurfaceVariant
|
||||
visible: index < totalSteps - 1
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Smooth navigation buttons
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 44
|
||||
Layout.topMargin: Style.marginS
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: Style.marginM
|
||||
|
||||
NButton {
|
||||
text: "Skip Setup"
|
||||
outlined: true
|
||||
visible: currentStep === 0
|
||||
Layout.preferredHeight: 44
|
||||
onClicked: {
|
||||
root.completeSetup()
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NButton {
|
||||
text: "← Back"
|
||||
outlined: true
|
||||
visible: currentStep > 0
|
||||
Layout.preferredHeight: 44
|
||||
onClicked: {
|
||||
if (currentStep > 0) {
|
||||
currentStep--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NButton {
|
||||
text: currentStep === totalSteps - 1 ? "🎉 All Done!" : "Continue →"
|
||||
Layout.preferredHeight: 44
|
||||
onClicked: {
|
||||
if (currentStep < totalSteps - 1) {
|
||||
currentStep++
|
||||
} else {
|
||||
root.completeSetup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function completeSetup() {
|
||||
Logger.log("SetupWizard", "Completing setup with selected options")
|
||||
|
||||
if (selectedWallpaperDirectory !== Settings.data.wallpaper.directory) {
|
||||
Settings.data.wallpaper.directory = selectedWallpaperDirectory
|
||||
WallpaperService.refreshWallpapersList()
|
||||
}
|
||||
|
||||
if (selectedWallpaper !== "") {
|
||||
WallpaperService.changeWallpaper(selectedWallpaper, undefined)
|
||||
}
|
||||
|
||||
Settings.data.general.scaleRatio = selectedScaleRatio
|
||||
Settings.data.bar.position = selectedBarPosition
|
||||
Settings.data.general.dimDesktop = selectedDimDesktop
|
||||
Settings.data.general.setupCompleted = true
|
||||
|
||||
Settings.saveImmediate()
|
||||
Logger.log("SetupWizard", "Setup completed successfully")
|
||||
root.close()
|
||||
}
|
||||
|
||||
function applyWallpaperSettings() {
|
||||
if (selectedWallpaperDirectory !== Settings.data.wallpaper.directory) {
|
||||
Settings.data.wallpaper.directory = selectedWallpaperDirectory
|
||||
WallpaperService.refreshWallpapersList()
|
||||
}
|
||||
|
||||
if (selectedWallpaper !== "") {
|
||||
WallpaperService.changeWallpaper(selectedWallpaper, undefined)
|
||||
}
|
||||
}
|
||||
|
||||
function applyUISettings() {
|
||||
Settings.data.general.scaleRatio = selectedScaleRatio
|
||||
Settings.data.bar.position = selectedBarPosition
|
||||
Settings.data.general.dimDesktop = selectedDimDesktop
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Logger.log("SetupWizard", "Setup wizard opened")
|
||||
// Initialize selections from existing settings to avoid overwriting user config
|
||||
if (Settings && Settings.data) {
|
||||
selectedScaleRatio = Settings.data.general.scaleRatio
|
||||
selectedBarPosition = Settings.data.bar.position
|
||||
selectedDimDesktop = Settings.data.general.dimDesktop
|
||||
selectedWallpaperDirectory = Settings.data.wallpaper.directory || Settings.defaultWallpapersDirectory
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,8 +105,8 @@ Singleton {
|
||||
// Wallpaper Colors Generation
|
||||
// --------------------------------------------------------------------------------
|
||||
function generateFromWallpaper() {
|
||||
Logger.log("AppThemeService", "Generating from wallpaper on screen:", Screen.name)
|
||||
|
||||
// Logger.log("AppThemeService", "Generating from wallpaper on screen:", Screen.name)
|
||||
const wp = WallpaperService.getWallpaper(Screen.name).replace(/'/g, "'\\''")
|
||||
if (!wp) {
|
||||
Logger.error("AppThemeService", "No wallpaper found")
|
||||
|
||||
@@ -31,8 +31,9 @@ Image {
|
||||
}
|
||||
onCachePathChanged: {
|
||||
if (imageHash && cachePath) {
|
||||
// Try to load the cached version, failure will be detected below in onStatusChanged
|
||||
source = cachePath
|
||||
// Check if cache file exists before trying to load it
|
||||
cacheChecker.command = ["test", "-f", cachePath]
|
||||
cacheChecker.running = true
|
||||
}
|
||||
}
|
||||
onStatusChanged: {
|
||||
@@ -48,4 +49,19 @@ Image {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Check if cache file exists to avoid warnings
|
||||
Process {
|
||||
id: cacheChecker
|
||||
running: false
|
||||
onExited: function (exitCode) {
|
||||
if (exitCode === 0 && root.cachePath) {
|
||||
// Cache file exists, load it
|
||||
root.source = root.cachePath
|
||||
} else if (root.imagePath) {
|
||||
// Cache doesn't exist, load original directly
|
||||
root.source = root.imagePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26
shell.qml
26
shell.qml
@@ -40,6 +40,7 @@ import qs.Modules.OSD
|
||||
import qs.Modules.Settings
|
||||
import qs.Modules.Toast
|
||||
import qs.Modules.Wallpaper
|
||||
import qs.Modules.SetupWizard
|
||||
|
||||
ShellRoot {
|
||||
id: shellRoot
|
||||
@@ -89,6 +90,10 @@ ShellRoot {
|
||||
HooksService.init()
|
||||
BluetoothService.init()
|
||||
BatteryService.init()
|
||||
|
||||
if (Settings && Settings.data && Settings.data.general && !Settings.data.general.setupCompleted) {
|
||||
setupWizardLoader.active = true
|
||||
}
|
||||
}
|
||||
|
||||
Background {}
|
||||
@@ -166,6 +171,27 @@ ShellRoot {
|
||||
id: batteryPanel
|
||||
objectName: "batteryPanel"
|
||||
}
|
||||
|
||||
// Lazy-load Setup Wizard to save memory
|
||||
Component {
|
||||
id: setupWizardComponent
|
||||
SetupWizard {
|
||||
id: setupWizardPanel
|
||||
objectName: "setupWizardPanel"
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: setupWizardLoader
|
||||
active: false
|
||||
asynchronous: true
|
||||
sourceComponent: setupWizardComponent
|
||||
onLoaded: {
|
||||
if (setupWizardLoader.item && setupWizardLoader.item.open) {
|
||||
setupWizardLoader.item.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user