339 lines
11 KiB
QML
339 lines
11 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
import qs.Commons
|
|
import qs.Services
|
|
import qs.Widgets
|
|
import Quickshell.Io
|
|
|
|
ColumnLayout {
|
|
id: root
|
|
|
|
spacing: 0
|
|
|
|
// Helper function to get color from scheme file
|
|
function getSchemeColor(schemePath, colorKey) {
|
|
// Extract scheme name from path
|
|
var schemeName = schemePath.split("/").pop().replace(".json", "")
|
|
|
|
// Try to get from cached data first
|
|
if (schemeColorsCache[schemeName] && schemeColorsCache[schemeName][colorKey]) {
|
|
return schemeColorsCache[schemeName][colorKey]
|
|
}
|
|
|
|
// Return a default color if not cached yet
|
|
return "#000000"
|
|
}
|
|
|
|
// Cache for scheme colors
|
|
property var schemeColorsCache: ({})
|
|
|
|
// Scale properties for card animations
|
|
property real cardScaleLow: 0.95
|
|
property real cardScaleHigh: 1.0
|
|
|
|
// This function is called by the FileView Repeater when a scheme file is loaded
|
|
function schemeLoaded(schemeName, jsonData) {
|
|
var colors = {}
|
|
|
|
// Extract colors from JSON data
|
|
if (jsonData && typeof jsonData === 'object') {
|
|
colors.mPrimary = jsonData.mPrimary || jsonData.primary || "#000000"
|
|
colors.mSecondary = jsonData.mSecondary || jsonData.secondary || "#000000"
|
|
colors.mTertiary = jsonData.mTertiary || jsonData.tertiary || "#000000"
|
|
colors.mError = jsonData.mError || jsonData.error || "#ff0000"
|
|
colors.mSurface = jsonData.mSurface || jsonData.surface || "#ffffff"
|
|
colors.mOnSurface = jsonData.mOnSurface || jsonData.onSurface || "#000000"
|
|
colors.mOutline = jsonData.mOutline || jsonData.outline || "#666666"
|
|
} else {
|
|
// Default colors on failure
|
|
colors = {
|
|
"mPrimary": "#000000",
|
|
"mSecondary": "#000000",
|
|
"mTertiary": "#000000",
|
|
"mError": "#ff0000",
|
|
"mSurface": "#ffffff",
|
|
"mOnSurface": "#000000",
|
|
"mOutline": "#666666"
|
|
}
|
|
}
|
|
|
|
// Update the cache. This must be done by re-assigning the whole object to trigger updates.
|
|
var newCache = schemeColorsCache
|
|
newCache[schemeName] = colors
|
|
schemeColorsCache = newCache
|
|
}
|
|
|
|
// When the list of available schemes changes, clear the cache.
|
|
// The Repeater below will automatically re-create the FileViews.
|
|
Connections {
|
|
target: ColorSchemeService
|
|
function onSchemesChanged() {
|
|
schemeColorsCache = {}
|
|
}
|
|
}
|
|
|
|
// A non-visual Item to host the Repeater that loads the color scheme files.
|
|
Item {
|
|
visible: false
|
|
id: fileLoaders
|
|
|
|
Repeater {
|
|
model: ColorSchemeService.schemes
|
|
|
|
// The delegate is a Component, which correctly wraps the non-visual FileView
|
|
delegate: Item {
|
|
FileView {
|
|
path: modelData
|
|
blockLoading: true
|
|
onLoaded: {
|
|
var schemeName = path.split("/").pop().replace(".json", "")
|
|
try {
|
|
var jsonData = JSON.parse(text())
|
|
root.schemeLoaded(schemeName, jsonData)
|
|
} catch (e) {
|
|
Logger.warn("ColorSchemeTab", "Failed to parse JSON for scheme:", schemeName, e)
|
|
root.schemeLoaded(schemeName, null) // Load defaults on parse error
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// UI Code
|
|
ScrollView {
|
|
id: scrollView
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
padding: Style.marginMedium * scaling
|
|
clip: true
|
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
|
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
|
|
|
ColumnLayout {
|
|
width: scrollView.availableWidth
|
|
spacing: 0
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 0
|
|
}
|
|
|
|
ColumnLayout {
|
|
spacing: Style.marginLarge * scaling
|
|
Layout.fillWidth: true
|
|
|
|
// Use Matugen
|
|
NToggle {
|
|
label: "Use Matugen"
|
|
description: "Automatically generate colors based on your active wallpaper using Matugen"
|
|
checked: Settings.data.colorSchemes.useWallpaperColors
|
|
onToggled: checked => {
|
|
Settings.data.colorSchemes.useWallpaperColors = checked
|
|
if (Settings.data.colorSchemes.useWallpaperColors) {
|
|
ColorSchemeService.changedWallpaper()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Dark Mode Toggle
|
|
NToggle {
|
|
label: "Dark Mode"
|
|
description: "Generate dark theme colors when using Matugen. Disable for light theme."
|
|
checked: Settings.data.colorSchemes.darkMode
|
|
enabled: Settings.data.colorSchemes.useWallpaperColors
|
|
onToggled: checked => {
|
|
Settings.data.colorSchemes.darkMode = checked
|
|
if (Settings.data.colorSchemes.useWallpaperColors) {
|
|
ColorSchemeService.changedWallpaper()
|
|
}
|
|
}
|
|
}
|
|
|
|
NDivider {
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: Style.marginLarge * scaling
|
|
Layout.bottomMargin: Style.marginLarge * scaling
|
|
}
|
|
|
|
ColumnLayout {
|
|
spacing: Style.marginTiniest * scaling
|
|
Layout.fillWidth: true
|
|
|
|
NText {
|
|
text: "Predefined Color Schemes"
|
|
font.pointSize: Style.fontSizeLarge * scaling
|
|
font.weight: Style.fontWeightBold
|
|
color: Color.mOnSurface
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
NText {
|
|
text: "These color schemes only apply when 'Use Matugen' is disabled. When enabled, Matugen will generate colors based on your wallpaper instead. You can toggle between light and dark themes when using Matugen."
|
|
font.pointSize: Style.fontSizeSmall * scaling
|
|
color: Color.mOnSurface
|
|
Layout.fillWidth: true
|
|
wrapMode: Text.WordWrap
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
spacing: Style.marginTiny * scaling
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: Style.marginLarge * scaling
|
|
|
|
// Color Schemes Grid
|
|
GridLayout {
|
|
columns: 4
|
|
rowSpacing: Style.marginLarge * scaling
|
|
columnSpacing: Style.marginLarge * scaling
|
|
Layout.fillWidth: true
|
|
|
|
Repeater {
|
|
model: ColorSchemeService.schemes
|
|
|
|
Rectangle {
|
|
id: schemeCard
|
|
|
|
property string schemePath: modelData
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 120 * scaling
|
|
radius: Style.radiusMedium * scaling
|
|
color: getSchemeColor(modelData, "mSurface")
|
|
border.width: Math.max(1, Style.borderThick * scaling)
|
|
border.color: Settings.data.colorSchemes.predefinedScheme === modelData ? Color.mPrimary : Color.mOutline
|
|
scale: root.cardScaleLow
|
|
|
|
// Mouse area for selection
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: {
|
|
// Disable useWallpaperColors when picking a predefined color scheme
|
|
// TBC: broken uncheck useWallpaperColors
|
|
Settings.data.colorSchemes.useWallpaperColors = false
|
|
Settings.data.colorSchemes.predefinedScheme = schemePath
|
|
ColorSchemeService.applyScheme(schemePath)
|
|
}
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
|
|
onEntered: {
|
|
schemeCard.scale = root.cardScaleHigh
|
|
}
|
|
|
|
onExited: {
|
|
schemeCard.scale = root.cardScaleLow
|
|
}
|
|
}
|
|
|
|
// Card content
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: Style.marginXL * scaling
|
|
spacing: Style.marginSmall * scaling
|
|
|
|
// Scheme name
|
|
NText {
|
|
text: {
|
|
// Remove json and the full path
|
|
var chunks = schemePath.replace(".json", "").split("/")
|
|
return chunks[chunks.length - 1]
|
|
}
|
|
font.pointSize: Style.fontSizeMedium * scaling
|
|
font.weight: Style.fontWeightBold
|
|
color: getSchemeColor(modelData, "mOnSurface")
|
|
Layout.fillWidth: true
|
|
elide: Text.ElideRight
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
|
|
// Color swatches
|
|
RowLayout {
|
|
spacing: Style.marginSmall * scaling
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignHCenter
|
|
|
|
// Primary color swatch
|
|
Rectangle {
|
|
width: 28 * scaling
|
|
height: 28 * scaling
|
|
radius: width * 0.5
|
|
color: getSchemeColor(modelData, "mPrimary")
|
|
}
|
|
|
|
// Secondary color swatch
|
|
Rectangle {
|
|
width: 28 * scaling
|
|
height: 28 * scaling
|
|
radius: width * 0.5
|
|
color: getSchemeColor(modelData, "mSecondary")
|
|
}
|
|
|
|
// Tertiary color swatch
|
|
Rectangle {
|
|
width: 28 * scaling
|
|
height: 28 * scaling
|
|
radius: width * 0.5
|
|
color: getSchemeColor(modelData, "mTertiary")
|
|
}
|
|
|
|
// Error color swatch
|
|
Rectangle {
|
|
width: 28 * scaling
|
|
height: 28 * scaling
|
|
radius: width * 0.5
|
|
color: getSchemeColor(modelData, "mError")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Selection indicator
|
|
Rectangle {
|
|
visible: Settings.data.colorSchemes.predefinedScheme === schemePath
|
|
anchors.right: parent.right
|
|
anchors.top: parent.top
|
|
anchors.margins: Style.marginSmall * scaling
|
|
width: 24 * scaling
|
|
height: 24 * scaling
|
|
radius: width * 0.5
|
|
color: Color.mPrimary
|
|
|
|
NText {
|
|
anchors.centerIn: parent
|
|
text: "✓"
|
|
font.pointSize: Style.fontSizeSmall * scaling
|
|
font.weight: Style.fontWeightBold
|
|
color: Color.mOnPrimary
|
|
}
|
|
}
|
|
|
|
// Smooth animations
|
|
Behavior on scale {
|
|
NumberAnimation {
|
|
duration: Style.animationNormal
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
|
|
Behavior on border.color {
|
|
ColorAnimation {
|
|
duration: Style.animationNormal
|
|
}
|
|
}
|
|
|
|
Behavior on border.width {
|
|
NumberAnimation {
|
|
duration: Style.animationFast
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|