fedora
This commit is contained in:
+395
@@ -0,0 +1,395 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services.System
|
||||
import qs.Services.Theming
|
||||
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.i("SetupAppearanceStep", `Loaded scheme ${schemeName}`);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ColorSchemeService
|
||||
function onSchemesChanged() {
|
||||
Logger.i("SetupAppearanceStep", `Color schemes changed: ${ColorSchemeService.schemes.length}`);
|
||||
schemeColorsCache = {};
|
||||
cacheVersion++;
|
||||
}
|
||||
}
|
||||
|
||||
// Beautiful header with icon
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: Style.marginL
|
||||
spacing: Style.marginM
|
||||
|
||||
Rectangle {
|
||||
width: 40
|
||||
height: 40
|
||||
radius: Style.radiusL
|
||||
color: Color.mSurfaceVariant
|
||||
opacity: 0.6
|
||||
|
||||
NIcon {
|
||||
icon: "palette"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginXS
|
||||
|
||||
NText {
|
||||
text: I18n.tr("common.appearance")
|
||||
pointSize: Style.fontSizeXL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("setup.appearance.subheader")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NScrollView {
|
||||
id: appearanceScrollView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
horizontalPolicy: ScrollBar.AlwaysOff
|
||||
verticalPolicy: ScrollBar.AsNeeded
|
||||
|
||||
ColumnLayout {
|
||||
width: appearanceScrollView.availableWidth
|
||||
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("tooltips.switch-to-dark-mode")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("panels.color-scheme.dark-mode-switch-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("panels.color-scheme.color-source-use-wallpaper-colors-label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("panels.color-scheme.color-source-use-wallpaper-colors-description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
NToggle {
|
||||
checked: Settings.data.colorSchemes.useWallpaperColors
|
||||
onToggled: checked => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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("panels.color-scheme.predefined-title")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("panels.color-scheme.predefined-desc")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Predefined schemes Grid (matches ColorSchemeTab)
|
||||
GridLayout {
|
||||
id: schemesGrid
|
||||
columns: Math.max(2, Math.floor((parent.width - Style.margin2M) / 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.cacheVersion >= 0 ? root.getSchemeColor(schemeName, "mSurface") : root.getSchemeColor(schemeName, "mSurface")
|
||||
border.width: Style.borderL
|
||||
border.color: itemMouseArea.containsMouse ? Color.mHover : (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.cacheVersion >= 0 ? (function () {
|
||||
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");
|
||||
})() : Color.mPrimary
|
||||
}
|
||||
Rectangle {
|
||||
width: 14
|
||||
height: 14
|
||||
radius: width * 0.5
|
||||
color: root.cacheVersion >= 0 ? (function () {
|
||||
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");
|
||||
})() : Color.mSecondary
|
||||
}
|
||||
Rectangle {
|
||||
width: 14
|
||||
height: 14
|
||||
radius: width * 0.5
|
||||
color: root.cacheVersion >= 0 ? (function () {
|
||||
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");
|
||||
})() : Color.mTertiary
|
||||
}
|
||||
Rectangle {
|
||||
width: 14
|
||||
height: 14
|
||||
radius: width * 0.5
|
||||
color: root.cacheVersion >= 0 ? (function () {
|
||||
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");
|
||||
})() : Color.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+617
@@ -0,0 +1,617 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property real selectedScaleRatio: 1.0
|
||||
property string selectedBarPosition: "top"
|
||||
|
||||
signal scaleRatioChanged(real ratio)
|
||||
signal barPositionChanged(string position)
|
||||
|
||||
spacing: Style.marginM
|
||||
|
||||
// Beautiful header with icon
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: Style.marginL
|
||||
spacing: Style.marginM
|
||||
|
||||
Rectangle {
|
||||
width: 40
|
||||
height: 40
|
||||
radius: Style.radiusL
|
||||
color: Color.mSurfaceVariant
|
||||
opacity: 0.6
|
||||
|
||||
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.mPrimary
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("setup.customize.subheader")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NScrollView {
|
||||
id: customizeScrollView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
horizontalPolicy: ScrollBar.AlwaysOff
|
||||
verticalPolicy: ScrollBar.AsNeeded
|
||||
|
||||
ColumnLayout {
|
||||
width: customizeScrollView.availableWidth
|
||||
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("panels.bar.appearance-position-label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("panels.bar.appearance-position-description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
{
|
||||
"key": "top",
|
||||
"name": I18n.tr("positions.top"),
|
||||
"icon": "arrow-up"
|
||||
},
|
||||
{
|
||||
"key": "bottom",
|
||||
"name": I18n.tr("positions.bottom"),
|
||||
"icon": "arrow-down"
|
||||
},
|
||||
{
|
||||
"key": "left",
|
||||
"name": I18n.tr("positions.left"),
|
||||
"icon": "arrow-left"
|
||||
},
|
||||
{
|
||||
"key": "right",
|
||||
"name": I18n.tr("positions.right"),
|
||||
"icon": "arrow-right"
|
||||
}
|
||||
]
|
||||
delegate: Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
radius: Style.radiusM
|
||||
border.width: Style.borderS
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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("panels.bar.appearance-density-label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
NText {
|
||||
text: I18n.tr("panels.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")
|
||||
},
|
||||
{
|
||||
"key": "spacious",
|
||||
"name": I18n.tr("options.bar.density-spacious")
|
||||
}
|
||||
]
|
||||
delegate: Rectangle {
|
||||
radius: Style.radiusM
|
||||
border.width: Style.borderS
|
||||
Layout.preferredHeight: 32
|
||||
Layout.preferredWidth: Math.max(90, densityText.implicitWidth + Style.margin2XL)
|
||||
|
||||
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("panels.user-interface.scaling-label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
NText {
|
||||
text: I18n.tr("panels.user-interface.scaling-description")
|
||||
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) + "%"
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Color.mOutline
|
||||
opacity: 0.2
|
||||
Layout.topMargin: Style.marginS
|
||||
Layout.bottomMargin: Style.marginS
|
||||
}
|
||||
|
||||
// Bar Type 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("panels.bar.appearance-type-label") ?? "Bar Type"
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("panels.bar.appearance-type-description") ?? "Choose the style of the bar: Simple, Floating or Framed"
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
{
|
||||
"key": "simple",
|
||||
"name": I18n.tr("options.bar.type-simple") ?? "Simple"
|
||||
},
|
||||
{
|
||||
"key": "floating",
|
||||
"name": I18n.tr("options.bar.type-floating") ?? "Floating"
|
||||
},
|
||||
{
|
||||
"key": "framed",
|
||||
"name": I18n.tr("options.bar.type-framed") ?? "Framed"
|
||||
}
|
||||
]
|
||||
delegate: Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
radius: Style.radiusM
|
||||
border.width: Style.borderS
|
||||
|
||||
property bool isActive: Settings.data.bar.barType === 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.bar.barType = 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
|
||||
}
|
||||
|
||||
// Divider
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Color.mOutline
|
||||
opacity: 0.2
|
||||
Layout.topMargin: Style.marginS
|
||||
Layout.bottomMargin: Style.marginS
|
||||
}
|
||||
|
||||
// Dim Desktop opacity
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
Rectangle {
|
||||
width: 32
|
||||
height: 32
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
NIcon {
|
||||
icon: "screen-share"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
NText {
|
||||
text: I18n.tr("panels.user-interface.dimmer-opacity-label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
NText {
|
||||
text: I18n.tr("panels.user-interface.dimmer-opacity-description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginXS
|
||||
from: 0
|
||||
to: 1
|
||||
stepSize: 0.01
|
||||
value: Settings.data.general.dimmerOpacity
|
||||
onMoved: value => Settings.data.general.dimmerOpacity = value
|
||||
text: Math.floor(Settings.data.general.dimmerOpacity * 100) + "%"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Color.mOutline
|
||||
opacity: 0.2
|
||||
Layout.topMargin: Style.marginS
|
||||
Layout.bottomMargin: Style.marginS
|
||||
}
|
||||
|
||||
// Drop Shadows toggle
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
Rectangle {
|
||||
width: 32
|
||||
height: 32
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
NIcon {
|
||||
icon: "shadow"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
NText {
|
||||
text: I18n.tr("panels.user-interface.shadows-label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
NText {
|
||||
text: I18n.tr("panels.user-interface.shadows-description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
NToggle {
|
||||
checked: Settings.data.general.enableShadows
|
||||
onToggled: checked => Settings.data.general.enableShadows = checked
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.marginL
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Services.Compositor
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
spacing: Style.marginM
|
||||
|
||||
// Header
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: Style.marginL
|
||||
spacing: Style.marginM
|
||||
|
||||
Rectangle {
|
||||
width: 40
|
||||
height: 40
|
||||
radius: Style.radiusL
|
||||
color: Color.mSurfaceVariant
|
||||
opacity: 0.6
|
||||
|
||||
NIcon {
|
||||
icon: "device-desktop"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginXS
|
||||
|
||||
NText {
|
||||
text: I18n.tr("panels.dock.title") || "Dock"
|
||||
pointSize: Style.fontSizeXL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("panels.dock.monitors-desc")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Options
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginL
|
||||
|
||||
NToggle {
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("panels.dock.enabled-label")
|
||||
description: I18n.tr("panels.dock.enabled-description")
|
||||
checked: Settings.data.dock.enabled
|
||||
onToggled: checked => Settings.data.dock.enabled = checked
|
||||
}
|
||||
|
||||
// Display behavior
|
||||
NComboBox {
|
||||
visible: Settings.data.dock.enabled
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("panels.display.title")
|
||||
description: I18n.tr("panels.dock.appearance-display-description")
|
||||
model: [
|
||||
{
|
||||
"key": "always_visible",
|
||||
"name": I18n.tr("hide-modes.visible")
|
||||
},
|
||||
{
|
||||
"key": "auto_hide",
|
||||
"name": I18n.tr("panels.dock.appearance-display-auto-hide")
|
||||
},
|
||||
{
|
||||
"key": "exclusive",
|
||||
"name": I18n.tr("panels.dock.appearance-display-exclusive")
|
||||
}
|
||||
]
|
||||
currentKey: Settings.data.dock.displayMode
|
||||
onSelected: key => Settings.data.dock.displayMode = key
|
||||
}
|
||||
|
||||
// Background opacity
|
||||
ColumnLayout {
|
||||
visible: Settings.data.dock.enabled
|
||||
spacing: Style.marginXXS
|
||||
Layout.fillWidth: true
|
||||
NLabel {
|
||||
label: I18n.tr("panels.osd.background-opacity-label")
|
||||
description: I18n.tr("panels.dock.appearance-background-opacity-description")
|
||||
}
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 1
|
||||
stepSize: 0.01
|
||||
value: Settings.data.dock.backgroundOpacity
|
||||
onMoved: value => Settings.data.dock.backgroundOpacity = value
|
||||
text: Math.floor(Settings.data.dock.backgroundOpacity * 100) + "%"
|
||||
}
|
||||
}
|
||||
|
||||
// Floating distance
|
||||
ColumnLayout {
|
||||
visible: Settings.data.dock.enabled
|
||||
spacing: Style.marginXXS
|
||||
Layout.fillWidth: true
|
||||
NLabel {
|
||||
label: I18n.tr("panels.dock.appearance-floating-distance-label")
|
||||
description: I18n.tr("panels.dock.appearance-floating-distance-description")
|
||||
}
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 4
|
||||
stepSize: 0.01
|
||||
value: Settings.data.dock.floatingRatio
|
||||
onMoved: value => Settings.data.dock.floatingRatio = value
|
||||
text: Math.floor(Settings.data.dock.floatingRatio * 100) + "%"
|
||||
}
|
||||
}
|
||||
|
||||
// Icon size
|
||||
ColumnLayout {
|
||||
visible: Settings.data.dock.enabled
|
||||
spacing: Style.marginXXS
|
||||
Layout.fillWidth: true
|
||||
NLabel {
|
||||
label: I18n.tr("panels.dock.appearance-icon-size-label")
|
||||
description: I18n.tr("panels.dock.appearance-icon-size-description")
|
||||
}
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 2
|
||||
stepSize: 0.01
|
||||
value: Settings.data.dock.size
|
||||
onMoved: value => Settings.data.dock.size = value
|
||||
text: Math.floor(Settings.data.dock.size * 100) + "%"
|
||||
}
|
||||
}
|
||||
|
||||
NToggle {
|
||||
visible: Settings.data.dock.enabled
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("panels.dock.monitors-only-same-monitor-label")
|
||||
description: I18n.tr("panels.dock.monitors-only-same-monitor-description")
|
||||
checked: Settings.data.dock.onlySameOutput
|
||||
onToggled: checked => Settings.data.dock.onlySameOutput = checked
|
||||
}
|
||||
|
||||
NToggle {
|
||||
visible: Settings.data.dock.enabled
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("panels.dock.appearance-colorize-icons-label")
|
||||
description: I18n.tr("panels.dock.appearance-colorize-icons-description")
|
||||
checked: Settings.data.dock.colorizeIcons
|
||||
onToggled: checked => Settings.data.dock.colorizeIcons = checked
|
||||
}
|
||||
|
||||
NHeader {
|
||||
visible: Settings.data.dock.enabled
|
||||
label: I18n.tr("panels.dock.monitors-title")
|
||||
description: I18n.tr("panels.dock.monitors-desc")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
visible: Settings.data.dock.enabled
|
||||
model: Quickshell.screens || []
|
||||
delegate: NCheckbox {
|
||||
Layout.fillWidth: true
|
||||
readonly property real compositorScale: {
|
||||
const info = CompositorService.displayScales[modelData.name];
|
||||
return (info && info.scale) ? info.scale : 1.0;
|
||||
}
|
||||
label: modelData.name || "Unknown"
|
||||
visible: Settings.data.dock.enabled
|
||||
description: {
|
||||
I18n.tr("system.monitor-description", {
|
||||
"model": modelData.model,
|
||||
"width": modelData.width * compositorScale,
|
||||
"height": modelData.height * compositorScale,
|
||||
"scale": compositorScale
|
||||
});
|
||||
}
|
||||
checked: (Settings.data.dock.monitors || []).indexOf(modelData.name) !== -1
|
||||
onToggled: checked => {
|
||||
if (checked) {
|
||||
const arr = (Settings.data.dock.monitors || []).slice();
|
||||
if (arr.indexOf(modelData.name) === -1)
|
||||
arr.push(modelData.name);
|
||||
Settings.data.dock.monitors = arr;
|
||||
} else {
|
||||
Settings.data.dock.monitors = (Settings.data.dock.monitors || []).filter(function (n) {
|
||||
return n !== modelData.name;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+463
@@ -0,0 +1,463 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services.UI
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property string selectedDirectory: ""
|
||||
property string selectedWallpaper: ""
|
||||
property string previewCachedPath: ""
|
||||
|
||||
signal directoryChanged(string directory)
|
||||
signal wallpaperChanged(string wallpaper)
|
||||
|
||||
onSelectedWallpaperChanged: {
|
||||
if (!selectedWallpaper) {
|
||||
previewCachedPath = "";
|
||||
return;
|
||||
}
|
||||
if (ImageCacheService.initialized) {
|
||||
ImageCacheService.getLarge(selectedWallpaper, Screen.width, Screen.height, function (path, success) {
|
||||
if (root)
|
||||
root.previewCachedPath = success ? path : selectedWallpaper;
|
||||
});
|
||||
} else {
|
||||
previewCachedPath = selectedWallpaper;
|
||||
}
|
||||
}
|
||||
|
||||
spacing: Style.marginL
|
||||
|
||||
// Beautiful header with icon
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: Style.marginL
|
||||
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.mPrimary
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("setup.wallpaper.subheader")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Large preview area
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumHeight: 180
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusL
|
||||
|
||||
// Image with rounded corners
|
||||
NImageRounded {
|
||||
anchors.fill: parent
|
||||
visible: previewCachedPath !== ""
|
||||
imagePath: previewCachedPath
|
||||
radius: Style.radiusL
|
||||
borderColor: previewCachedPath !== "" ? Color.mPrimary : Color.mOutline
|
||||
borderWidth: previewCachedPath !== "" ? 2 : 1
|
||||
imageFillMode: Image.PreserveAspectCrop
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: Style.marginL
|
||||
visible: selectedWallpaper === ""
|
||||
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
width: 64
|
||||
height: 64
|
||||
radius: width / 2
|
||||
color: Color.mPrimary
|
||||
|
||||
NIcon {
|
||||
icon: "sparkles"
|
||||
pointSize: Style.fontSizeXXL
|
||||
color: Color.mOnPrimary
|
||||
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: 92
|
||||
visible: filteredWallpapers.length > 0
|
||||
|
||||
NScrollView {
|
||||
id: galleryScroll
|
||||
anchors.fill: parent
|
||||
horizontalPolicy: ScrollBar.AsNeeded
|
||||
verticalPolicy: ScrollBar.AlwaysOff
|
||||
showGradientMasks: false
|
||||
reserveScrollbarSpace: false
|
||||
|
||||
// Enable vertical mouse wheel to scroll the horizontal strip by moving contentX
|
||||
WheelHandler {
|
||||
target: galleryScroll.contentItem
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
onWheel: event => {
|
||||
const flick = galleryScroll.contentItem;
|
||||
if (!flick)
|
||||
return;
|
||||
const delta = event.pixelDelta.x !== 0 || event.pixelDelta.y !== 0 ? (event.pixelDelta.y !== 0 ? event.pixelDelta.y : event.pixelDelta.x) : (event.angleDelta.y !== 0 ? event.angleDelta.y : event.angleDelta.x);
|
||||
// Move opposite of wheel to scroll content to the right for wheel down
|
||||
const step = -delta;
|
||||
const maxX = Math.max(0, flick.contentWidth - flick.width);
|
||||
let newX = flick.contentX + step;
|
||||
if (newX < 0)
|
||||
newX = 0;
|
||||
if (newX > maxX)
|
||||
newX = maxX;
|
||||
flick.contentX = newX;
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.marginM
|
||||
height: parent.height
|
||||
|
||||
Repeater {
|
||||
model: filteredWallpapers
|
||||
delegate: Item {
|
||||
id: thumbDelegate
|
||||
readonly property int baseWidth: 120
|
||||
readonly property int baseHeight: 80
|
||||
readonly property int thumbRadius: Style.radiusM
|
||||
property string cachedPath: ""
|
||||
|
||||
Layout.preferredWidth: baseWidth
|
||||
Layout.preferredHeight: baseHeight
|
||||
|
||||
Component.onCompleted: {
|
||||
if (ImageCacheService.initialized) {
|
||||
ImageCacheService.getThumbnail(modelData, function (path, success) {
|
||||
if (thumbDelegate)
|
||||
thumbDelegate.cachedPath = success ? path : modelData;
|
||||
});
|
||||
} else {
|
||||
cachedPath = modelData;
|
||||
}
|
||||
}
|
||||
|
||||
// Rounded thumbnail using NImageRounded with caching
|
||||
NImageRounded {
|
||||
id: thumbImage
|
||||
anchors.fill: parent
|
||||
imagePath: thumbDelegate.cachedPath
|
||||
radius: thumbDelegate.thumbRadius
|
||||
borderColor: selectedWallpaper === modelData ? Color.mPrimary : Color.mOutline
|
||||
borderWidth: selectedWallpaper === modelData ? 2 : 1
|
||||
imageFillMode: Image.PreserveAspectCrop
|
||||
}
|
||||
|
||||
// Loading/error state background
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.mSurfaceVariant
|
||||
radius: thumbDelegate.thumbRadius
|
||||
visible: thumbImage.status === Image.Loading || thumbImage.status === Image.Error || thumbDelegate.cachedPath === ""
|
||||
|
||||
NIcon {
|
||||
icon: "image"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mOnSurfaceVariant
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
NBusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
visible: thumbImage.status === Image.Loading || thumbDelegate.cachedPath === ""
|
||||
running: visible
|
||||
size: 18
|
||||
}
|
||||
|
||||
// Hover overlay
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: thumbDelegate.thumbRadius
|
||||
color: Color.mPrimary
|
||||
opacity: hoverHandler.hovered ? 0.1 : 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Selection badge
|
||||
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
|
||||
onInputTextChanged: text => {
|
||||
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) {
|
||||
// Build find command args dynamically from ImageCacheService filters
|
||||
var filters = ImageCacheService.imageFilters;
|
||||
var findArgs = ["find", directoryPath, "-type", "f", "("];
|
||||
for (var i = 0; i < filters.length; i++) {
|
||||
if (i > 0) {
|
||||
findArgs.push("-o");
|
||||
}
|
||||
findArgs.push("-iname");
|
||||
findArgs.push(filters[i]);
|
||||
}
|
||||
findArgs.push(")");
|
||||
directoryScanner.command = findArgs;
|
||||
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: []
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Modules.MainScreen
|
||||
import qs.Services.Noctalia
|
||||
import qs.Services.System
|
||||
import qs.Services.UI
|
||||
import qs.Widgets
|
||||
|
||||
SmartPanel {
|
||||
id: root
|
||||
|
||||
// When true, only shows step 0 with modified text for returning users (telemetry notification)
|
||||
property bool telemetryOnlyMode: false
|
||||
|
||||
signal telemetryWizardCompleted
|
||||
|
||||
preferredWidth: Math.round(preferredWidthRatio * 2560 * Style.uiScaleRatio)
|
||||
preferredHeight: Math.round(preferredHeightRatio * 1440 * Style.uiScaleRatio)
|
||||
preferredWidthRatio: 0.4
|
||||
preferredHeightRatio: root.telemetryOnlyMode ? 0.45 : 0.6
|
||||
|
||||
panelAnchorHorizontalCenter: true
|
||||
panelAnchorVerticalCenter: true
|
||||
|
||||
closeWithEscape: false
|
||||
|
||||
panelContent: Item {
|
||||
id: panelContent
|
||||
|
||||
// Wizard state (lazy-loaded with panelContent)
|
||||
property int currentStep: 0
|
||||
readonly property int totalSteps: root.telemetryOnlyMode ? 1 : 5
|
||||
property bool isCompleting: false
|
||||
|
||||
// Setup wizard data
|
||||
property string selectedWallpaperDirectory: Settings.defaultWallpapersDirectory
|
||||
property string selectedWallpaper: ""
|
||||
property real selectedScaleRatio: 1.0
|
||||
property string selectedBarPosition: "top"
|
||||
|
||||
Component.onCompleted: {
|
||||
selectedScaleRatio = Settings.data.general.scaleRatio;
|
||||
selectedBarPosition = Settings.data.bar.position;
|
||||
selectedWallpaperDirectory = Settings.data.wallpaper.directory || Settings.defaultWallpapersDirectory;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Settings
|
||||
function onSettingsSaved() {
|
||||
if (panelContent.isCompleting) {
|
||||
Logger.i("SetupWizard", "Settings saved, closing panel");
|
||||
panelContent.isCompleting = false;
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: closeTimer
|
||||
interval: 2000
|
||||
onTriggered: {
|
||||
if (panelContent.isCompleting) {
|
||||
Logger.w("SetupWizard", "Settings save timeout, closing panel anyway");
|
||||
panelContent.isCompleting = false;
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function completeSetup() {
|
||||
if (isCompleting) {
|
||||
Logger.w("SetupWizard", "completeSetup() called while already completing, ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Logger.i("SetupWizard", root.telemetryOnlyMode ? "Completing telemetry wizard" : "Completing setup with selected options");
|
||||
isCompleting = true;
|
||||
|
||||
// In telemetry-only mode, we only need to save the telemetry setting
|
||||
if (!root.telemetryOnlyMode) {
|
||||
if (typeof WallpaperService !== "undefined" && WallpaperService.refreshWallpapersList) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Mark the current version as seen to prevent telemetry wizard on next startup
|
||||
// (only for full setup wizard - telemetry wizard lets changelog mark it seen)
|
||||
if (!root.telemetryOnlyMode) {
|
||||
UpdateService.markChangelogSeen(UpdateService.currentVersion);
|
||||
}
|
||||
|
||||
// Initialize telemetry now that user has made their choice
|
||||
TelemetryService.init();
|
||||
|
||||
// Save settings immediately and wait for settingsSaved signal before closing
|
||||
Settings.saveImmediate();
|
||||
Logger.i("SetupWizard", "Setup completed successfully, waiting for settings save confirmation");
|
||||
|
||||
// Emit signal for telemetry wizard completion (shell.qml will show changelog)
|
||||
if (root.telemetryOnlyMode) {
|
||||
root.telemetryWizardCompleted();
|
||||
}
|
||||
|
||||
// Fallback: if settingsSaved signal doesn't fire within 2 seconds, close anyway
|
||||
closeTimer.start();
|
||||
} catch (error) {
|
||||
Logger.e("SetupWizard", "Error completing setup:", error);
|
||||
isCompleting = false;
|
||||
}
|
||||
}
|
||||
|
||||
function applyWallpaperSettings() {
|
||||
if (typeof WallpaperService !== "undefined" && WallpaperService.refreshWallpapersList) {
|
||||
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;
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: wizardContent
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginXL
|
||||
spacing: Style.marginL
|
||||
|
||||
// Step content - takes most of the space
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumHeight: Math.round(300 * Style.uiScaleRatio)
|
||||
|
||||
StackLayout {
|
||||
id: stepStack
|
||||
anchors.fill: parent
|
||||
currentIndex: currentStep
|
||||
|
||||
// Step 0: Welcome - Beautiful centered design
|
||||
Item {
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
width: Math.round(Math.max(parent.width * 0.5, 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: Qt.resolvedUrl(Quickshell.shellDir + "/Assets/noctalia.svg")
|
||||
fillMode: Image.PreserveAspectFit
|
||||
smooth: true
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.mSurfaceVariant
|
||||
radius: width / 2
|
||||
border.color: Color.mOutline
|
||||
border.width: Style.borderM
|
||||
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: root.telemetryOnlyMode ? I18n.tr("setup.telemetry-wizard-title") : I18n.tr("setup.welcome-title")
|
||||
pointSize: Style.fontSizeXXL * 1.4
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: root.telemetryOnlyMode ? I18n.tr("setup.telemetry-wizard-subtitle") : I18n.tr("setup.welcome-subtitle")
|
||||
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.margin2M
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusL
|
||||
|
||||
NText {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Style.margin2L
|
||||
text: root.telemetryOnlyMode ? I18n.tr("setup.telemetry-wizard-note") : I18n.tr("setup.welcome-note")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurfaceVariant
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
// Telemetry toggle
|
||||
NToggle {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginM
|
||||
label: I18n.tr("panels.about.telemetry-enabled")
|
||||
description: I18n.tr("panels.about.telemetry-desc")
|
||||
checked: Settings.data.general.telemetryEnabled
|
||||
onToggled: checked => Settings.data.general.telemetryEnabled = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1: Wallpaper Setup
|
||||
SetupWallpaperStep {
|
||||
id: step1
|
||||
selectedDirectory: panelContent.selectedWallpaperDirectory
|
||||
selectedWallpaper: panelContent.selectedWallpaper
|
||||
onDirectoryChanged: function (directory) {
|
||||
panelContent.selectedWallpaperDirectory = directory;
|
||||
panelContent.applyWallpaperSettings();
|
||||
}
|
||||
onWallpaperChanged: function (wallpaper) {
|
||||
panelContent.selectedWallpaper = wallpaper;
|
||||
panelContent.applyWallpaperSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Appearance - Dark mode and color source
|
||||
SetupAppearanceStep {
|
||||
id: step3
|
||||
}
|
||||
|
||||
// Step 3: UI Configuration
|
||||
SetupCustomizeStep {
|
||||
id: step2
|
||||
selectedScaleRatio: panelContent.selectedScaleRatio
|
||||
selectedBarPosition: panelContent.selectedBarPosition
|
||||
onScaleRatioChanged: function (ratio) {
|
||||
panelContent.selectedScaleRatio = ratio;
|
||||
panelContent.applyUISettings();
|
||||
}
|
||||
onBarPositionChanged: function (position) {
|
||||
panelContent.selectedBarPosition = position;
|
||||
panelContent.applyUISettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Dock Setup
|
||||
SetupDockStep {
|
||||
id: stepDock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Elegant divider
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Color.mOutline
|
||||
opacity: 0.2
|
||||
visible: !root.telemetryOnlyMode
|
||||
}
|
||||
|
||||
// Modern progress indicator with labels
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 32
|
||||
visible: !root.telemetryOnlyMode
|
||||
|
||||
RowLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: Style.marginM
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
{
|
||||
"icon": "sparkles",
|
||||
"label": I18n.tr("setup.welcome")
|
||||
},
|
||||
{
|
||||
"icon": "image",
|
||||
"label": I18n.tr("common.wallpaper")
|
||||
},
|
||||
{
|
||||
"icon": "palette",
|
||||
"label": I18n.tr("common.appearance")
|
||||
},
|
||||
{
|
||||
"icon": "settings",
|
||||
"label": I18n.tr("common.customize")
|
||||
},
|
||||
{
|
||||
"icon": "device-desktop",
|
||||
"label": I18n.tr("panels.dock.title")
|
||||
}
|
||||
]
|
||||
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: I18n.tr("setup.skip-setup")
|
||||
outlined: true
|
||||
visible: !root.telemetryOnlyMode
|
||||
Layout.preferredHeight: 44
|
||||
onClicked: {
|
||||
panelContent.completeSetup();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NButton {
|
||||
text: "← " + I18n.tr("common.back")
|
||||
outlined: true
|
||||
visible: currentStep > 0 && !root.telemetryOnlyMode
|
||||
Layout.preferredHeight: 44
|
||||
onClicked: {
|
||||
if (currentStep > 0) {
|
||||
currentStep--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NButton {
|
||||
text: root.telemetryOnlyMode ? I18n.tr("setup.telemetry-wizard-done") : (currentStep === totalSteps - 1 ? I18n.tr("setup.all-done") : I18n.tr("common.continue") + " →")
|
||||
Layout.preferredHeight: 44
|
||||
onClicked: {
|
||||
if (currentStep < totalSteps - 1) {
|
||||
currentStep++;
|
||||
} else {
|
||||
panelContent.completeSetup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user