Files
noctalia-shell/Commons/Color.qml
T

458 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 resolveOnColorKey(key) {
switch (key) {
case "primary":
return root.mOnPrimary;
case "secondary":
return root.mOnSecondary;
case "tertiary":
return root.mOnTertiary;
case "error":
return root.mOnError;
default:
return root.mSurface;
}
}
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
}
}
}