mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
443 lines
12 KiB
QML
443 lines
12 KiB
QML
pragma Singleton
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import qs.Commons
|
|
|
|
/*
|
|
Noctalia is not strictly a Material Design project, it supports both some predefined
|
|
color schemes and dynamic color generation from the wallpaper.
|
|
|
|
We ultimately decided to use a restricted set of colors that follows the
|
|
Material Design 3 naming convention.
|
|
|
|
NOTE: All color names are prefixed with 'm' (e.g., mPrimary) to prevent QML from
|
|
misinterpreting them as signals (e.g., the 'onPrimary' property name).
|
|
*/
|
|
Singleton {
|
|
id: root
|
|
|
|
property bool reloadColors: false
|
|
|
|
// Suppress transition animations until the first colors.json load completes
|
|
property bool skipTransition: true
|
|
|
|
// Flag indicating theme colors are currently transitioning (for widgets to disable their own animations)
|
|
property bool isTransitioning: false
|
|
|
|
// Timer to reset isTransitioning after animation completes
|
|
Timer {
|
|
id: transitionTimer
|
|
interval: Style.animationSlowest + 50 // Small buffer after animation
|
|
onTriggered: root.isTransitioning = false
|
|
}
|
|
|
|
// --- Key Colors: These are the main accent colors that define your app's style
|
|
property color mPrimary: defaultColors.mPrimary
|
|
property color mOnPrimary: defaultColors.mOnPrimary
|
|
property color mSecondary: defaultColors.mSecondary
|
|
property color mOnSecondary: defaultColors.mOnSecondary
|
|
property color mTertiary: defaultColors.mTertiary
|
|
property color mOnTertiary: defaultColors.mOnTertiary
|
|
|
|
// --- Utility Colors: These colors serve specific, universal purposes like indicating errors
|
|
property color mError: defaultColors.mError
|
|
property color mOnError: defaultColors.mOnError
|
|
|
|
// --- Surface and Variant Colors: These provide additional options for surfaces and their contents, creating visual hierarchy
|
|
property color mSurface: defaultColors.mSurface
|
|
property color mOnSurface: defaultColors.mOnSurface
|
|
|
|
property color mSurfaceVariant: defaultColors.mSurfaceVariant
|
|
property color mOnSurfaceVariant: defaultColors.mOnSurfaceVariant
|
|
|
|
property color mOutline: defaultColors.mOutline
|
|
property color mShadow: defaultColors.mShadow
|
|
|
|
property color mHover: defaultColors.mHover
|
|
property color mOnHover: defaultColors.mOnHover
|
|
|
|
// --- Color transition animations ---
|
|
Behavior on mPrimary {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mOnPrimary {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mSecondary {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mOnSecondary {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mTertiary {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mOnTertiary {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mError {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mOnError {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mSurface {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mOnSurface {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mSurfaceVariant {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mOnSurfaceVariant {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mOutline {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mShadow {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mHover {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on mOnHover {
|
|
enabled: !root.skipTransition
|
|
ColorAnimation {
|
|
duration: Style.animationSlowest
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
|
|
// Helper to start transition and update a color
|
|
function startTransition() {
|
|
root.isTransitioning = true;
|
|
transitionTimer.restart();
|
|
}
|
|
|
|
// Update colors when customColorsData changes (imperative assignment enables Behavior animations)
|
|
Connections {
|
|
target: customColorsData
|
|
function onMPrimaryChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mPrimary = customColorsData.mPrimary;
|
|
}
|
|
function onMOnPrimaryChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mOnPrimary = customColorsData.mOnPrimary;
|
|
}
|
|
function onMSecondaryChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mSecondary = customColorsData.mSecondary;
|
|
}
|
|
function onMOnSecondaryChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mOnSecondary = customColorsData.mOnSecondary;
|
|
}
|
|
function onMTertiaryChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mTertiary = customColorsData.mTertiary;
|
|
}
|
|
function onMOnTertiaryChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mOnTertiary = customColorsData.mOnTertiary;
|
|
}
|
|
function onMErrorChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mError = customColorsData.mError;
|
|
}
|
|
function onMOnErrorChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mOnError = customColorsData.mOnError;
|
|
}
|
|
function onMSurfaceChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mSurface = customColorsData.mSurface;
|
|
}
|
|
function onMOnSurfaceChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mOnSurface = customColorsData.mOnSurface;
|
|
}
|
|
function onMSurfaceVariantChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mSurfaceVariant = customColorsData.mSurfaceVariant;
|
|
}
|
|
function onMOnSurfaceVariantChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mOnSurfaceVariant = customColorsData.mOnSurfaceVariant;
|
|
}
|
|
function onMOutlineChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mOutline = customColorsData.mOutline;
|
|
}
|
|
function onMShadowChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mShadow = customColorsData.mShadow;
|
|
}
|
|
function onMHoverChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mHover = customColorsData.mHover;
|
|
}
|
|
function onMOnHoverChanged() {
|
|
if (!root.skipTransition) {
|
|
startTransition();
|
|
}
|
|
root.mOnHover = customColorsData.mOnHover;
|
|
}
|
|
}
|
|
|
|
function resolveColorKey(key) {
|
|
switch (key) {
|
|
case "primary":
|
|
return root.mPrimary;
|
|
case "secondary":
|
|
return root.mSecondary;
|
|
case "tertiary":
|
|
return root.mTertiary;
|
|
case "error":
|
|
return root.mError;
|
|
default:
|
|
return root.mOnSurface;
|
|
}
|
|
}
|
|
|
|
function resolveColorKeyOptional(key) {
|
|
switch (key) {
|
|
case "primary":
|
|
return root.mPrimary;
|
|
case "secondary":
|
|
return root.mSecondary;
|
|
case "tertiary":
|
|
return root.mTertiary;
|
|
case "error":
|
|
return root.mError;
|
|
default:
|
|
return "transparent";
|
|
}
|
|
}
|
|
|
|
readonly property var colorKeyModel: [
|
|
{
|
|
"key": "none",
|
|
"name": I18n.tr("common.none")
|
|
},
|
|
{
|
|
"key": "primary",
|
|
"name": I18n.tr("common.primary")
|
|
},
|
|
{
|
|
"key": "secondary",
|
|
"name": I18n.tr("common.secondary")
|
|
},
|
|
{
|
|
"key": "tertiary",
|
|
"name": I18n.tr("common.tertiary")
|
|
},
|
|
{
|
|
"key": "error",
|
|
"name": I18n.tr("common.error")
|
|
}
|
|
]
|
|
|
|
// --------------------------------
|
|
// Default colors: Noctalia (default) dark — must match Assets/ColorScheme/Noctalia-default
|
|
QtObject {
|
|
id: defaultColors
|
|
|
|
readonly property color mPrimary: "#fff59b"
|
|
readonly property color mOnPrimary: "#0e0e43"
|
|
|
|
readonly property color mSecondary: "#a9aefe"
|
|
readonly property color mOnSecondary: "#0e0e43"
|
|
|
|
readonly property color mTertiary: "#9BFECE"
|
|
readonly property color mOnTertiary: "#0e0e43"
|
|
|
|
readonly property color mError: "#FD4663"
|
|
readonly property color mOnError: "#0e0e43"
|
|
|
|
readonly property color mSurface: "#070722"
|
|
readonly property color mOnSurface: "#f3edf7"
|
|
|
|
readonly property color mSurfaceVariant: "#11112d"
|
|
readonly property color mOnSurfaceVariant: "#7c80b4"
|
|
|
|
readonly property color mOutline: "#21215F"
|
|
readonly property color mShadow: "#070722"
|
|
|
|
readonly property color mHover: "#9BFECE"
|
|
readonly property color mOnHover: "#0e0e43"
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
// FileView to load custom colors data from colors.json
|
|
FileView {
|
|
id: customColorsFile
|
|
path: Settings.directoriesCreated ? (Settings.configDir + "colors.json") : undefined
|
|
printErrors: false
|
|
watchChanges: true
|
|
onFileChanged: {
|
|
Logger.d("Color", "Reloading colors from disk");
|
|
reloadColors = true;
|
|
reload();
|
|
}
|
|
onAdapterUpdated: {
|
|
Logger.d("Color", "Writing colors to disk");
|
|
writeAdapter();
|
|
}
|
|
|
|
onLoaded: {
|
|
if (root.skipTransition) {
|
|
Qt.callLater(function () {
|
|
root.skipTransition = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
// Trigger initial load when path changes from empty to actual path
|
|
onPathChanged: {
|
|
if (path !== undefined) {
|
|
reload();
|
|
}
|
|
}
|
|
onLoadFailed: function (error) {
|
|
if (reloadColors) {
|
|
reloadColors = false;
|
|
return;
|
|
}
|
|
|
|
if (root.skipTransition) {
|
|
Qt.callLater(function () {
|
|
root.skipTransition = false;
|
|
});
|
|
}
|
|
|
|
// Error code 2 = ENOENT (No such file or directory)
|
|
if (error === 2 || error.toString().includes("No such file")) {
|
|
// File doesn't exist, create it with default values
|
|
writeAdapter();
|
|
}
|
|
}
|
|
JsonAdapter {
|
|
id: customColorsData
|
|
|
|
property color mPrimary: defaultColors.mPrimary
|
|
property color mOnPrimary: defaultColors.mOnPrimary
|
|
|
|
property color mSecondary: defaultColors.mSecondary
|
|
property color mOnSecondary: defaultColors.mOnSecondary
|
|
|
|
property color mTertiary: defaultColors.mTertiary
|
|
property color mOnTertiary: defaultColors.mOnTertiary
|
|
|
|
property color mError: defaultColors.mError
|
|
property color mOnError: defaultColors.mOnError
|
|
|
|
property color mSurface: defaultColors.mSurface
|
|
property color mOnSurface: defaultColors.mOnSurface
|
|
|
|
property color mSurfaceVariant: defaultColors.mSurfaceVariant
|
|
property color mOnSurfaceVariant: defaultColors.mOnSurfaceVariant
|
|
|
|
property color mOutline: defaultColors.mOutline
|
|
property color mShadow: defaultColors.mShadow
|
|
|
|
property color mHover: defaultColors.mHover
|
|
property color mOnHover: defaultColors.mOnHover
|
|
}
|
|
}
|
|
}
|