dynamic color scheme progress
This commit is contained in:
@@ -3,3 +3,4 @@
|
|||||||
.qmlls.ini
|
.qmlls.ini
|
||||||
build/
|
build/
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
testpython
|
||||||
|
|||||||
@@ -39,9 +39,13 @@ Scope {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: backgroundRect
|
id: backgroundRect
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Config.baseBgColor
|
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surface : Config.baseBgColor
|
||||||
radius: 0
|
radius: 0
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: 5
|
anchors.leftMargin: 5
|
||||||
@@ -92,11 +96,17 @@ Scope {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: notificationCenterIcon
|
id: notificationCenterIcon
|
||||||
|
property color iconColor: Config.useDynamicColors ? DynamicColors.palette.m3tertiaryFixed : "white"
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
text: HasNotifications.hasNotifications ? "\uf4fe" : "\ue7f4"
|
text: HasNotifications.hasNotifications ? "\uf4fe" : "\ue7f4"
|
||||||
font.family: "Material Symbols Rounded"
|
font.family: "Material Symbols Rounded"
|
||||||
font.pixelSize: 20
|
font.pixelSize: 20
|
||||||
color: "white"
|
color: iconColor
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import qs.Config
|
||||||
|
import qs.Modules
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: Time.time
|
text: Time.time
|
||||||
color: "white"
|
color: Config.useDynamicColors ? DynamicColors.palette.m3tertiary : "white"
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Singleton {
|
|||||||
property alias colors: adapter.colors
|
property alias colors: adapter.colors
|
||||||
property alias gpuType: adapter.gpuType
|
property alias gpuType: adapter.gpuType
|
||||||
property alias background: adapter.background
|
property alias background: adapter.background
|
||||||
|
property alias useDynamicColors: adapter.useDynamicColors
|
||||||
|
|
||||||
FileView {
|
FileView {
|
||||||
id: root
|
id: root
|
||||||
@@ -42,6 +43,7 @@ Singleton {
|
|||||||
property Colors colors: Colors {}
|
property Colors colors: Colors {}
|
||||||
property string gpuType: ""
|
property string gpuType: ""
|
||||||
property BackgroundConfig background: BackgroundConfig {}
|
property BackgroundConfig background: BackgroundConfig {}
|
||||||
|
property bool useDynamicColors: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,217 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import QtQuick
|
||||||
|
import ZShell
|
||||||
|
import qs.Helpers
|
||||||
|
import qs.Paths
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool showPreview
|
||||||
|
property string scheme
|
||||||
|
property string flavour
|
||||||
|
readonly property bool light: showPreview ? previewLight : currentLight
|
||||||
|
property bool currentLight
|
||||||
|
property bool previewLight
|
||||||
|
readonly property M3Palette palette: showPreview ? preview : current
|
||||||
|
readonly property M3TPalette tPalette: M3TPalette {}
|
||||||
|
readonly property M3Palette current: M3Palette {}
|
||||||
|
readonly property M3Palette preview: M3Palette {}
|
||||||
|
readonly property Transparency transparency: Transparency {}
|
||||||
|
readonly property alias wallLuminance: analyser.luminance
|
||||||
|
|
||||||
|
function getLuminance(c: color): real {
|
||||||
|
if (c.r == 0 && c.g == 0 && c.b == 0)
|
||||||
|
return 0;
|
||||||
|
return Math.sqrt(0.299 * (c.r ** 2) + 0.587 * (c.g ** 2) + 0.114 * (c.b ** 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
function alterColor(c: color, a: real, layer: int): color {
|
||||||
|
const luminance = getLuminance(c);
|
||||||
|
|
||||||
|
const offset = (!light || layer == 1 ? 1 : -layer / 2) * (light ? 0.2 : 0.3) * (1 - transparency.base) * (1 + wallLuminance * (light ? (layer == 1 ? 3 : 1) : 2.5));
|
||||||
|
const scale = (luminance + offset) / luminance;
|
||||||
|
const r = Math.max(0, Math.min(1, c.r * scale));
|
||||||
|
const g = Math.max(0, Math.min(1, c.g * scale));
|
||||||
|
const b = Math.max(0, Math.min(1, c.b * scale));
|
||||||
|
|
||||||
|
return Qt.rgba(r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
function layer(c: color, layer: var): color {
|
||||||
|
if (!transparency.enabled)
|
||||||
|
return c;
|
||||||
|
|
||||||
|
return layer === 0 ? Qt.alpha(c, transparency.base) : alterColor(c, transparency.layers, layer ?? 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function on(c: color): color {
|
||||||
|
if (c.hslLightness < 0.5)
|
||||||
|
return Qt.hsla(c.hslHue, c.hslSaturation, 0.9, 1);
|
||||||
|
return Qt.hsla(c.hslHue, c.hslSaturation, 0.1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function load(data: string, isPreview: bool): void {
|
||||||
|
const colors = isPreview ? preview : current;
|
||||||
|
const scheme = JSON.parse(data);
|
||||||
|
|
||||||
|
if (!isPreview) {
|
||||||
|
root.scheme = scheme.name;
|
||||||
|
flavour = scheme.flavour;
|
||||||
|
currentLight = scheme.mode === "light";
|
||||||
|
} else {
|
||||||
|
previewLight = scheme.mode === "light";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [name, color] of Object.entries(scheme.colors)) {
|
||||||
|
const propName = name.startsWith("term") ? name : `m3${name}`;
|
||||||
|
if (colors.hasOwnProperty(propName))
|
||||||
|
colors[propName] = `${color}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
path: `${Paths.state}/scheme.json`
|
||||||
|
watchChanges: true
|
||||||
|
onFileChanged: reload()
|
||||||
|
onLoaded: root.load(text(), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageAnalyser {
|
||||||
|
id: analyser
|
||||||
|
|
||||||
|
source: WallpaperPath.currentWallpaperPath
|
||||||
|
}
|
||||||
|
|
||||||
|
component Transparency: QtObject {
|
||||||
|
readonly property bool enabled: true
|
||||||
|
readonly property real base: 0.85 - (root.light ? 0.1 : 0)
|
||||||
|
readonly property real layers: 0.4
|
||||||
|
}
|
||||||
|
|
||||||
|
component M3TPalette: QtObject {
|
||||||
|
readonly property color m3primary_paletteKeyColor: root.layer(root.palette.m3primary_paletteKeyColor)
|
||||||
|
readonly property color m3secondary_paletteKeyColor: root.layer(root.palette.m3secondary_paletteKeyColor)
|
||||||
|
readonly property color m3tertiary_paletteKeyColor: root.layer(root.palette.m3tertiary_paletteKeyColor)
|
||||||
|
readonly property color m3neutral_paletteKeyColor: root.layer(root.palette.m3neutral_paletteKeyColor)
|
||||||
|
readonly property color m3neutral_variant_paletteKeyColor: root.layer(root.palette.m3neutral_variant_paletteKeyColor)
|
||||||
|
readonly property color m3background: root.layer(root.palette.m3background, 0)
|
||||||
|
readonly property color m3onBackground: root.layer(root.palette.m3onBackground)
|
||||||
|
readonly property color m3surface: root.layer(root.palette.m3surface, 0)
|
||||||
|
readonly property color m3surfaceDim: root.layer(root.palette.m3surfaceDim, 0)
|
||||||
|
readonly property color m3surfaceBright: root.layer(root.palette.m3surfaceBright, 0)
|
||||||
|
readonly property color m3surfaceContainerLowest: root.layer(root.palette.m3surfaceContainerLowest)
|
||||||
|
readonly property color m3surfaceContainerLow: root.layer(root.palette.m3surfaceContainerLow)
|
||||||
|
readonly property color m3surfaceContainer: root.layer(root.palette.m3surfaceContainer)
|
||||||
|
readonly property color m3surfaceContainerHigh: root.layer(root.palette.m3surfaceContainerHigh)
|
||||||
|
readonly property color m3surfaceContainerHighest: root.layer(root.palette.m3surfaceContainerHighest)
|
||||||
|
readonly property color m3onSurface: root.layer(root.palette.m3onSurface)
|
||||||
|
readonly property color m3surfaceVariant: root.layer(root.palette.m3surfaceVariant, 0)
|
||||||
|
readonly property color m3onSurfaceVariant: root.layer(root.palette.m3onSurfaceVariant)
|
||||||
|
readonly property color m3inverseSurface: root.layer(root.palette.m3inverseSurface, 0)
|
||||||
|
readonly property color m3inverseOnSurface: root.layer(root.palette.m3inverseOnSurface)
|
||||||
|
readonly property color m3outline: root.layer(root.palette.m3outline)
|
||||||
|
readonly property color m3outlineVariant: root.layer(root.palette.m3outlineVariant)
|
||||||
|
readonly property color m3shadow: root.layer(root.palette.m3shadow)
|
||||||
|
readonly property color m3scrim: root.layer(root.palette.m3scrim)
|
||||||
|
readonly property color m3surfaceTint: root.layer(root.palette.m3surfaceTint)
|
||||||
|
readonly property color m3primary: root.layer(root.palette.m3primary)
|
||||||
|
readonly property color m3onPrimary: root.layer(root.palette.m3onPrimary)
|
||||||
|
readonly property color m3primaryContainer: root.layer(root.palette.m3primaryContainer)
|
||||||
|
readonly property color m3onPrimaryContainer: root.layer(root.palette.m3onPrimaryContainer)
|
||||||
|
readonly property color m3inversePrimary: root.layer(root.palette.m3inversePrimary)
|
||||||
|
readonly property color m3secondary: root.layer(root.palette.m3secondary)
|
||||||
|
readonly property color m3onSecondary: root.layer(root.palette.m3onSecondary)
|
||||||
|
readonly property color m3secondaryContainer: root.layer(root.palette.m3secondaryContainer)
|
||||||
|
readonly property color m3onSecondaryContainer: root.layer(root.palette.m3onSecondaryContainer)
|
||||||
|
readonly property color m3tertiary: root.layer(root.palette.m3tertiary)
|
||||||
|
readonly property color m3onTertiary: root.layer(root.palette.m3onTertiary)
|
||||||
|
readonly property color m3tertiaryContainer: root.layer(root.palette.m3tertiaryContainer)
|
||||||
|
readonly property color m3onTertiaryContainer: root.layer(root.palette.m3onTertiaryContainer)
|
||||||
|
readonly property color m3error: root.layer(root.palette.m3error)
|
||||||
|
readonly property color m3onError: root.layer(root.palette.m3onError)
|
||||||
|
readonly property color m3errorContainer: root.layer(root.palette.m3errorContainer)
|
||||||
|
readonly property color m3onErrorContainer: root.layer(root.palette.m3onErrorContainer)
|
||||||
|
readonly property color m3success: root.layer(root.palette.m3success)
|
||||||
|
readonly property color m3onSuccess: root.layer(root.palette.m3onSuccess)
|
||||||
|
readonly property color m3successContainer: root.layer(root.palette.m3successContainer)
|
||||||
|
readonly property color m3onSuccessContainer: root.layer(root.palette.m3onSuccessContainer)
|
||||||
|
readonly property color m3primaryFixed: root.layer(root.palette.m3primaryFixed)
|
||||||
|
readonly property color m3primaryFixedDim: root.layer(root.palette.m3primaryFixedDim)
|
||||||
|
readonly property color m3onPrimaryFixed: root.layer(root.palette.m3onPrimaryFixed)
|
||||||
|
readonly property color m3onPrimaryFixedVariant: root.layer(root.palette.m3onPrimaryFixedVariant)
|
||||||
|
readonly property color m3secondaryFixed: root.layer(root.palette.m3secondaryFixed)
|
||||||
|
readonly property color m3secondaryFixedDim: root.layer(root.palette.m3secondaryFixedDim)
|
||||||
|
readonly property color m3onSecondaryFixed: root.layer(root.palette.m3onSecondaryFixed)
|
||||||
|
readonly property color m3onSecondaryFixedVariant: root.layer(root.palette.m3onSecondaryFixedVariant)
|
||||||
|
readonly property color m3tertiaryFixed: root.layer(root.palette.m3tertiaryFixed)
|
||||||
|
readonly property color m3tertiaryFixedDim: root.layer(root.palette.m3tertiaryFixedDim)
|
||||||
|
readonly property color m3onTertiaryFixed: root.layer(root.palette.m3onTertiaryFixed)
|
||||||
|
readonly property color m3onTertiaryFixedVariant: root.layer(root.palette.m3onTertiaryFixedVariant)
|
||||||
|
}
|
||||||
|
|
||||||
|
component M3Palette: QtObject {
|
||||||
|
property color m3primary_paletteKeyColor: "#a8627b"
|
||||||
|
property color m3secondary_paletteKeyColor: "#8e6f78"
|
||||||
|
property color m3tertiary_paletteKeyColor: "#986e4c"
|
||||||
|
property color m3neutral_paletteKeyColor: "#807477"
|
||||||
|
property color m3neutral_variant_paletteKeyColor: "#837377"
|
||||||
|
property color m3background: "#191114"
|
||||||
|
property color m3onBackground: "#efdfe2"
|
||||||
|
property color m3surface: "#191114"
|
||||||
|
property color m3surfaceDim: "#191114"
|
||||||
|
property color m3surfaceBright: "#403739"
|
||||||
|
property color m3surfaceContainerLowest: "#130c0e"
|
||||||
|
property color m3surfaceContainerLow: "#22191c"
|
||||||
|
property color m3surfaceContainer: "#261d20"
|
||||||
|
property color m3surfaceContainerHigh: "#31282a"
|
||||||
|
property color m3surfaceContainerHighest: "#3c3235"
|
||||||
|
property color m3onSurface: "#efdfe2"
|
||||||
|
property color m3surfaceVariant: "#514347"
|
||||||
|
property color m3onSurfaceVariant: "#d5c2c6"
|
||||||
|
property color m3inverseSurface: "#efdfe2"
|
||||||
|
property color m3inverseOnSurface: "#372e30"
|
||||||
|
property color m3outline: "#9e8c91"
|
||||||
|
property color m3outlineVariant: "#514347"
|
||||||
|
property color m3shadow: "#000000"
|
||||||
|
property color m3scrim: "#000000"
|
||||||
|
property color m3surfaceTint: "#ffb0ca"
|
||||||
|
property color m3primary: "#ffb0ca"
|
||||||
|
property color m3onPrimary: "#541d34"
|
||||||
|
property color m3primaryContainer: "#6f334a"
|
||||||
|
property color m3onPrimaryContainer: "#ffd9e3"
|
||||||
|
property color m3inversePrimary: "#8b4a62"
|
||||||
|
property color m3secondary: "#e2bdc7"
|
||||||
|
property color m3onSecondary: "#422932"
|
||||||
|
property color m3secondaryContainer: "#5a3f48"
|
||||||
|
property color m3onSecondaryContainer: "#ffd9e3"
|
||||||
|
property color m3tertiary: "#f0bc95"
|
||||||
|
property color m3onTertiary: "#48290c"
|
||||||
|
property color m3tertiaryContainer: "#b58763"
|
||||||
|
property color m3onTertiaryContainer: "#000000"
|
||||||
|
property color m3error: "#ffb4ab"
|
||||||
|
property color m3onError: "#690005"
|
||||||
|
property color m3errorContainer: "#93000a"
|
||||||
|
property color m3onErrorContainer: "#ffdad6"
|
||||||
|
property color m3success: "#B5CCBA"
|
||||||
|
property color m3onSuccess: "#213528"
|
||||||
|
property color m3successContainer: "#374B3E"
|
||||||
|
property color m3onSuccessContainer: "#D1E9D6"
|
||||||
|
property color m3primaryFixed: "#ffd9e3"
|
||||||
|
property color m3primaryFixedDim: "#ffb0ca"
|
||||||
|
property color m3onPrimaryFixed: "#39071f"
|
||||||
|
property color m3onPrimaryFixedVariant: "#6f334a"
|
||||||
|
property color m3secondaryFixed: "#ffd9e3"
|
||||||
|
property color m3secondaryFixedDim: "#e2bdc7"
|
||||||
|
property color m3onSecondaryFixed: "#2b151d"
|
||||||
|
property color m3onSecondaryFixedVariant: "#5a3f48"
|
||||||
|
property color m3tertiaryFixed: "#ffdcc3"
|
||||||
|
property color m3tertiaryFixedDim: "#f0bc95"
|
||||||
|
property color m3onTertiaryFixed: "#2f1500"
|
||||||
|
property color m3onTertiaryFixedVariant: "#623f21"
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-8
@@ -5,6 +5,7 @@ import Quickshell.Io
|
|||||||
import Quickshell.Services.Pipewire
|
import Quickshell.Services.Pipewire
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import qs.Modules
|
import qs.Modules
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -12,6 +13,7 @@ Item {
|
|||||||
implicitHeight: 34
|
implicitHeight: 34
|
||||||
|
|
||||||
property bool expanded: false
|
property bool expanded: false
|
||||||
|
property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3tertiaryFixed : "#ffffff"
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
@@ -34,8 +36,11 @@ Item {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
height: 22
|
height: 22
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
color: "#40000000"
|
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surfaceContainer : "#40000000"
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
|
|
||||||
// Background circle
|
// Background circle
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -66,8 +71,11 @@ Item {
|
|||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
font.family: "Material Symbols Rounded"
|
font.family: "Material Symbols Rounded"
|
||||||
font.pixelSize: 18
|
font.pixelSize: 18
|
||||||
text: "\ue050" // volume_up icon
|
text: "\ue050" // volumeUp icon
|
||||||
color: "#ffffff"
|
color: root.textColor
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -87,7 +95,7 @@ Item {
|
|||||||
|
|
||||||
implicitWidth: parent.width * ( Pipewire.defaultAudioSink?.audio.volume ?? 0 )
|
implicitWidth: parent.width * ( Pipewire.defaultAudioSink?.audio.volume ?? 0 )
|
||||||
radius: parent.radius
|
radius: parent.radius
|
||||||
color: "#ffffff"
|
color: root.textColor
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -97,7 +105,7 @@ Item {
|
|||||||
width: sinkVolumeMouseArea.pressed ? 25 : 12
|
width: sinkVolumeMouseArea.pressed ? 25 : 12
|
||||||
height: sinkVolumeMouseArea.pressed ? 25 : 12
|
height: sinkVolumeMouseArea.pressed ? 25 : 12
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
color: sinkVolumeMouseArea.containsMouse || sinkVolumeMouseArea.pressed ? "#ffffff" : "#aaaaaa"
|
color: sinkVolumeMouseArea.containsMouse || sinkVolumeMouseArea.pressed ? (Config.useDynamicColors ? DynamicColors.palette.m3onSurface : "#ffffff") : (Config.useDynamicColors ? DynamicColors.palette.m3onSurfaceVariant : "#aaaaaa")
|
||||||
border.color: "#40000000"
|
border.color: "#40000000"
|
||||||
border.width: 2
|
border.width: 2
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -171,7 +179,11 @@ Item {
|
|||||||
font.family: "Material Symbols Rounded"
|
font.family: "Material Symbols Rounded"
|
||||||
font.pixelSize: 18
|
font.pixelSize: 18
|
||||||
text: "\ue029"
|
text: "\ue029"
|
||||||
color: ( Pipewire.defaultAudioSource?.audio.muted ?? false ) ? "#ff4444" : "#ffffff"
|
color: ( Pipewire.defaultAudioSource?.audio.muted ?? false ) ? (Config.useDynamicColors ? DynamicColors.palette.m3error : "#ff4444") : root.textColor
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -191,7 +203,11 @@ Item {
|
|||||||
|
|
||||||
implicitWidth: parent.width * ( Pipewire.defaultAudioSource?.audio.volume ?? 0 )
|
implicitWidth: parent.width * ( Pipewire.defaultAudioSource?.audio.volume ?? 0 )
|
||||||
radius: parent.radius
|
radius: parent.radius
|
||||||
color: ( Pipewire.defaultAudioSource?.audio.muted ?? false ) ? "#ff4444" : "#ffffff"
|
color: ( Pipewire.defaultAudioSource?.audio.muted ?? false ) ? (Config.useDynamicColors ? DynamicColors.palette.m3error : "#ff4444") : root.textColor
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -201,7 +217,7 @@ Item {
|
|||||||
width: sourceVolumeMouseArea.pressed ? 25 : 12
|
width: sourceVolumeMouseArea.pressed ? 25 : 12
|
||||||
height: sourceVolumeMouseArea.pressed ? 25 : 12
|
height: sourceVolumeMouseArea.pressed ? 25 : 12
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
color: sourceVolumeMouseArea.containsMouse || sourceVolumeMouseArea.pressed ? "#ffffff" : "#aaaaaa"
|
color: sourceVolumeMouseArea.containsMouse || sourceVolumeMouseArea.pressed ? (Config.useDynamicColors ? DynamicColors.palette.m3onSurface : "#ffffff") : (Config.useDynamicColors ? DynamicColors.palette.m3onSurfaceVariant : "#aaaaaa")
|
||||||
border.color: "#40000000"
|
border.color: "#40000000"
|
||||||
border.width: 2
|
border.width: 2
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Modules
|
||||||
|
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 400
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: MaterialEasing.standard
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import QtQuick
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
import qs.Config
|
import qs.Config
|
||||||
|
import qs.Paths
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: root
|
id: root
|
||||||
@@ -84,6 +85,8 @@ TextField {
|
|||||||
launcherWindow.visible = false;
|
launcherWindow.visible = false;
|
||||||
} else if ( wallpaperPickerLoader.active ) {
|
} else if ( wallpaperPickerLoader.active ) {
|
||||||
SearchWallpapers.setWallpaper(wallpaperPickerLoader.item.currentItem.modelData.path)
|
SearchWallpapers.setWallpaper(wallpaperPickerLoader.item.currentItem.modelData.path)
|
||||||
|
if ( Config.useDynamicColors )
|
||||||
|
Quickshell.execDetached(["python3", Quickshell.shellPath("scripts/SchemeColorGen.py"), `--path=${wallpaperPickerLoader.item.currentItem.modelData.path}`, `--thumbnail=${Paths.cache}/imagecache/thumbnail.jpg`, `--output=${Paths.state}/scheme.json`]);
|
||||||
if ( Config.wallust ) {
|
if ( Config.wallust ) {
|
||||||
Wallust.generateColors(WallpaperPath.currentWallpaperPath);
|
Wallust.generateColors(WallpaperPath.currentWallpaperPath);
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-2
@@ -15,6 +15,8 @@ Item {
|
|||||||
implicitWidth: resourceRowLayout.x < 0 ? 0 : resourceRowLayout.implicitWidth
|
implicitWidth: resourceRowLayout.x < 0 ? 0 : resourceRowLayout.implicitWidth
|
||||||
implicitHeight: 22
|
implicitHeight: 22
|
||||||
property bool warning: percentage * 100 >= warningThreshold
|
property bool warning: percentage * 100 >= warningThreshold
|
||||||
|
property color usageColor: Config.useDynamicColors ? ( warning ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary ) : ( warning ? Config.accentColor.accents.warning : Config.accentColor.accents.primary )
|
||||||
|
property color borderColor: Config.useDynamicColors ? ( warning ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary ) : ( warning ? Config.accentColor.accents.warningAlt : Config.accentColor.accents.primaryAlt )
|
||||||
|
|
||||||
Behavior on percentage {
|
Behavior on percentage {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
@@ -55,9 +57,14 @@ Item {
|
|||||||
|
|
||||||
ShapePath {
|
ShapePath {
|
||||||
strokeWidth: 0
|
strokeWidth: 0
|
||||||
fillColor: root.warning ? Config.accentColor.accents.warning : Config.accentColor.accents.primary
|
fillColor: root.usageColor
|
||||||
startX: backgroundCircle.width / 2
|
startX: backgroundCircle.width / 2
|
||||||
startY: backgroundCircle.height / 2
|
startY: backgroundCircle.height / 2
|
||||||
|
|
||||||
|
Behavior on fillColor {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
|
|
||||||
PathLine {
|
PathLine {
|
||||||
x: backgroundCircle.width / 2
|
x: backgroundCircle.width / 2
|
||||||
y: 0 + ( 1 / 2 )
|
y: 0 + ( 1 / 2 )
|
||||||
@@ -80,10 +87,14 @@ Item {
|
|||||||
|
|
||||||
ShapePath {
|
ShapePath {
|
||||||
strokeWidth: 1
|
strokeWidth: 1
|
||||||
strokeColor: root.warning ? Config.accentColor.accents.warningAlt : Config.accentColor.accents.primaryAlt
|
strokeColor: root.borderColor
|
||||||
fillColor: "transparent"
|
fillColor: "transparent"
|
||||||
capStyle: ShapePath.FlatCap
|
capStyle: ShapePath.FlatCap
|
||||||
|
|
||||||
|
Behavior on strokeColor {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
|
|
||||||
PathAngleArc {
|
PathAngleArc {
|
||||||
centerX: backgroundCircle.width / 2
|
centerX: backgroundCircle.width / 2
|
||||||
centerY: backgroundCircle.height / 2
|
centerY: backgroundCircle.height / 2
|
||||||
|
|||||||
+18
-5
@@ -10,6 +10,7 @@ Item {
|
|||||||
id: root
|
id: root
|
||||||
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
|
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
|
||||||
implicitHeight: 34
|
implicitHeight: 34
|
||||||
|
property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3tertiaryFixed : "#ffffff"
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors {
|
anchors {
|
||||||
@@ -18,7 +19,7 @@ Item {
|
|||||||
verticalCenter: parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
implicitHeight: 22
|
implicitHeight: 22
|
||||||
color: "#40000000"
|
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surfaceContainer : "#40000000"
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: rowLayout
|
id: rowLayout
|
||||||
@@ -33,7 +34,10 @@ Item {
|
|||||||
font.family: "Material Symbols Rounded"
|
font.family: "Material Symbols Rounded"
|
||||||
font.pixelSize: 18
|
font.pixelSize: 18
|
||||||
text: "\uf7a3"
|
text: "\uf7a3"
|
||||||
color: "#ffffff"
|
color: root.textColor
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
@@ -46,7 +50,10 @@ Item {
|
|||||||
font.family: "Material Symbols Rounded"
|
font.family: "Material Symbols Rounded"
|
||||||
font.pixelSize: 18
|
font.pixelSize: 18
|
||||||
text: "\ue322"
|
text: "\ue322"
|
||||||
color: "#ffffff"
|
color: root.textColor
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
@@ -59,7 +66,10 @@ Item {
|
|||||||
font.family: "Material Symbols Rounded"
|
font.family: "Material Symbols Rounded"
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
text: "\ue30f"
|
text: "\ue30f"
|
||||||
color: "#ffffff"
|
color: root.textColor
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
@@ -71,7 +81,10 @@ Item {
|
|||||||
font.family: "Material Symbols Rounded"
|
font.family: "Material Symbols Rounded"
|
||||||
font.pixelSize: 18
|
font.pixelSize: 18
|
||||||
text: "\ue30d"
|
text: "\ue30d"
|
||||||
color: "#ffffff"
|
color: root.textColor
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import QtQuick
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Services.SystemTray
|
import Quickshell.Services.SystemTray
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Modules
|
import qs.Modules
|
||||||
import qs.Config
|
import qs.Config
|
||||||
|
import Caelestia
|
||||||
|
import QtQuick.Effects
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: root
|
id: root
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import qs.Modules
|
import qs.Modules
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
required property int countUpdates
|
required property int countUpdates
|
||||||
implicitWidth: contentRow.childrenRect.width + 10
|
implicitWidth: contentRow.childrenRect.width + 10
|
||||||
implicitHeight: 22
|
implicitHeight: 22
|
||||||
|
property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3tertiaryFixed : "#ffffff"
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
color: "#40000000"
|
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surfaceContainer : "#40000000"
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -28,7 +33,10 @@ Item {
|
|||||||
font.family: "Material Symbols Rounded"
|
font.family: "Material Symbols Rounded"
|
||||||
font.pixelSize: 18
|
font.pixelSize: 18
|
||||||
text: "\uf569"
|
text: "\uf569"
|
||||||
color: "#ffffff"
|
color: root.textColor
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextMetrics {
|
TextMetrics {
|
||||||
@@ -40,7 +48,10 @@ Item {
|
|||||||
Text {
|
Text {
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||||
text: textMetrics.text
|
text: textMetrics.text
|
||||||
color: "white"
|
color: root.textColor
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -2,6 +2,7 @@ import QtQuick
|
|||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -11,6 +12,7 @@ Item {
|
|||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
property bool showFirst: true
|
property bool showFirst: true
|
||||||
|
property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3primary : "white"
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Hyprland.rawEvent.connect(( event ) => {
|
Hyprland.rawEvent.connect(( event ) => {
|
||||||
@@ -37,7 +39,7 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 5
|
anchors.margins: 5
|
||||||
text: root.currentTitle
|
text: root.currentTitle
|
||||||
color: "white"
|
color: root.textColor
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
@@ -46,13 +48,17 @@ Item {
|
|||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }
|
NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: titleText2
|
id: titleText2
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 5
|
anchors.margins: 5
|
||||||
color: "white"
|
color: root.textColor
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
@@ -61,5 +67,9 @@ Item {
|
|||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }
|
NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-10
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
import qs.Config
|
import qs.Config
|
||||||
@@ -26,7 +27,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
color: "#40000000"
|
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surfaceContainer : "#40000000"
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
@@ -36,6 +37,10 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
CAnim {}
|
||||||
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: workspacesRow
|
id: workspacesRow
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -47,15 +52,16 @@ Rectangle {
|
|||||||
model: Hyprland.workspaces
|
model: Hyprland.workspaces
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: workspaceIndicator
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|
||||||
width: 16
|
width: 16
|
||||||
height: 16
|
height: 16
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
|
|
||||||
color: modelData.id === Hyprland.focusedWorkspace.id ? Config.accentColor.accents.primary : "#606060"
|
color: modelData.id === Hyprland.focusedWorkspace.id ? ( Config.useDynamicColors ? DynamicColors.palette.m3primary : Config.accentColor.accents.primary ) : ( Config.useDynamicColors ? DynamicColors.palette.m3inverseOnSurface : "#606060" )
|
||||||
|
|
||||||
border.color: modelData.id === Hyprland.focusedWorkspace.id ? Config.accentColor.accents.primaryAlt : "#808080"
|
border.color: modelData.id === Hyprland.focusedWorkspace.id ? ( Config.useDynamicColors ? DynamicColors.palette.m3onPrimary : Config.accentColor.accents.primaryAlt ) : ( Config.useDynamicColors ? DynamicColors.palette.m3inverseOnSurface : "#808080" )
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
visible: root.shouldShow( modelData.monitor )
|
visible: root.shouldShow( modelData.monitor )
|
||||||
@@ -90,13 +96,13 @@ Rectangle {
|
|||||||
duration: 200
|
duration: 200
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
// Text {
|
||||||
anchors.centerIn: parent
|
// anchors.centerIn: parent
|
||||||
text: modelData.id
|
// text: modelData.id
|
||||||
font.pixelSize: 10
|
// font.pixelSize: 10
|
||||||
font.family: "Rubik"
|
// font.family: "Rubik"
|
||||||
color: modelData.id === Hyprland.focusedWorkspace.id ? Config.workspaceWidget.textColor : Config.workspaceWidget.inactiveTextColor
|
// color: modelData.id === Hyprland.focusedWorkspace.id ? Config.workspaceWidget.textColor : Config.workspaceWidget.inactiveTextColor
|
||||||
}
|
// }
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ qml_module(ZShell
|
|||||||
SOURCES
|
SOURCES
|
||||||
writefile.hpp writefile.cpp
|
writefile.hpp writefile.cpp
|
||||||
appdb.hpp appdb.cpp
|
appdb.hpp appdb.cpp
|
||||||
|
imageanalyser.hpp imageanalyser.cpp
|
||||||
LIBRARIES
|
LIBRARIES
|
||||||
Qt::Gui
|
Qt::Gui
|
||||||
Qt::Quick
|
Qt::Quick
|
||||||
|
|||||||
@@ -0,0 +1,223 @@
|
|||||||
|
#include "imageanalyser.hpp"
|
||||||
|
|
||||||
|
#include <QtConcurrent/qtconcurrentrun.h>
|
||||||
|
#include <QtQuick/qquickitemgrabresult.h>
|
||||||
|
#include <qfuturewatcher.h>
|
||||||
|
#include <qimage.h>
|
||||||
|
#include <qquickwindow.h>
|
||||||
|
|
||||||
|
namespace ZShell {
|
||||||
|
|
||||||
|
ImageAnalyser::ImageAnalyser(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_futureWatcher(new QFutureWatcher<AnalyseResult>(this))
|
||||||
|
, m_source("")
|
||||||
|
, m_sourceItem(nullptr)
|
||||||
|
, m_rescaleSize(128)
|
||||||
|
, m_dominantColour(0, 0, 0)
|
||||||
|
, m_luminance(0) {
|
||||||
|
QObject::connect(m_futureWatcher, &QFutureWatcher<AnalyseResult>::finished, this, [this]() {
|
||||||
|
if (!m_futureWatcher->future().isResultReadyAt(0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = m_futureWatcher->result();
|
||||||
|
if (m_dominantColour != result.first) {
|
||||||
|
m_dominantColour = result.first;
|
||||||
|
emit dominantColourChanged();
|
||||||
|
}
|
||||||
|
if (!qFuzzyCompare(m_luminance + 1.0, result.second + 1.0)) {
|
||||||
|
m_luminance = result.second;
|
||||||
|
emit luminanceChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ImageAnalyser::source() const {
|
||||||
|
return m_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageAnalyser::setSource(const QString& source) {
|
||||||
|
if (m_source == source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_source = source;
|
||||||
|
emit sourceChanged();
|
||||||
|
|
||||||
|
if (m_sourceItem) {
|
||||||
|
m_sourceItem = nullptr;
|
||||||
|
emit sourceItemChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem* ImageAnalyser::sourceItem() const {
|
||||||
|
return m_sourceItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageAnalyser::setSourceItem(QQuickItem* sourceItem) {
|
||||||
|
if (m_sourceItem == sourceItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sourceItem = sourceItem;
|
||||||
|
emit sourceItemChanged();
|
||||||
|
|
||||||
|
if (!m_source.isEmpty()) {
|
||||||
|
m_source = "";
|
||||||
|
emit sourceChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImageAnalyser::rescaleSize() const {
|
||||||
|
return m_rescaleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageAnalyser::setRescaleSize(int rescaleSize) {
|
||||||
|
if (m_rescaleSize == rescaleSize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_rescaleSize = rescaleSize;
|
||||||
|
emit rescaleSizeChanged();
|
||||||
|
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor ImageAnalyser::dominantColour() const {
|
||||||
|
return m_dominantColour;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal ImageAnalyser::luminance() const {
|
||||||
|
return m_luminance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageAnalyser::requestUpdate() {
|
||||||
|
if (m_source.isEmpty() && !m_sourceItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_sourceItem || (m_sourceItem->window() && m_sourceItem->window()->isVisible() && m_sourceItem->width() > 0 &&
|
||||||
|
m_sourceItem->height() > 0)) {
|
||||||
|
update();
|
||||||
|
} else if (m_sourceItem) {
|
||||||
|
if (!m_sourceItem->window()) {
|
||||||
|
QObject::connect(m_sourceItem, &QQuickItem::windowChanged, this, &ImageAnalyser::requestUpdate,
|
||||||
|
Qt::SingleShotConnection);
|
||||||
|
} else if (!m_sourceItem->window()->isVisible()) {
|
||||||
|
QObject::connect(m_sourceItem->window(), &QQuickWindow::visibleChanged, this, &ImageAnalyser::requestUpdate,
|
||||||
|
Qt::SingleShotConnection);
|
||||||
|
}
|
||||||
|
if (m_sourceItem->width() <= 0) {
|
||||||
|
QObject::connect(
|
||||||
|
m_sourceItem, &QQuickItem::widthChanged, this, &ImageAnalyser::requestUpdate, Qt::SingleShotConnection);
|
||||||
|
}
|
||||||
|
if (m_sourceItem->height() <= 0) {
|
||||||
|
QObject::connect(m_sourceItem, &QQuickItem::heightChanged, this, &ImageAnalyser::requestUpdate,
|
||||||
|
Qt::SingleShotConnection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageAnalyser::update() {
|
||||||
|
if (m_source.isEmpty() && !m_sourceItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_futureWatcher->isRunning()) {
|
||||||
|
m_futureWatcher->cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_sourceItem) {
|
||||||
|
const QSharedPointer<const QQuickItemGrabResult> grabResult = m_sourceItem->grabToImage();
|
||||||
|
QObject::connect(grabResult.data(), &QQuickItemGrabResult::ready, this, [grabResult, this]() {
|
||||||
|
m_futureWatcher->setFuture(QtConcurrent::run(&ImageAnalyser::analyse, grabResult->image(), m_rescaleSize));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
m_futureWatcher->setFuture(QtConcurrent::run([=, this](QPromise<AnalyseResult>& promise) {
|
||||||
|
const QImage image(m_source);
|
||||||
|
analyse(promise, image, m_rescaleSize);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageAnalyser::analyse(QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize) {
|
||||||
|
if (image.isNull()) {
|
||||||
|
qWarning() << "ImageAnalyser::analyse: image is null";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage img = image;
|
||||||
|
|
||||||
|
if (rescaleSize > 0 && (img.width() > rescaleSize || img.height() > rescaleSize)) {
|
||||||
|
img = img.scaled(rescaleSize, rescaleSize, Qt::KeepAspectRatio, Qt::FastTransformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (promise.isCanceled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (img.format() != QImage::Format_ARGB32) {
|
||||||
|
img = img.convertToFormat(QImage::Format_ARGB32);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (promise.isCanceled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uchar* data = img.bits();
|
||||||
|
const int width = img.width();
|
||||||
|
const int height = img.height();
|
||||||
|
const qsizetype bytesPerLine = img.bytesPerLine();
|
||||||
|
|
||||||
|
std::unordered_map<quint32, int> colours;
|
||||||
|
qreal totalLuminance = 0.0;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
const uchar* line = data + y * bytesPerLine;
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
if (promise.isCanceled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uchar* pixel = line + x * 4;
|
||||||
|
|
||||||
|
if (pixel[3] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const quint32 mr = static_cast<quint32>(pixel[0] & 0xF8);
|
||||||
|
const quint32 mg = static_cast<quint32>(pixel[1] & 0xF8);
|
||||||
|
const quint32 mb = static_cast<quint32>(pixel[2] & 0xF8);
|
||||||
|
++colours[(mr << 16) | (mg << 8) | mb];
|
||||||
|
|
||||||
|
const qreal r = pixel[0] / 255.0;
|
||||||
|
const qreal g = pixel[1] / 255.0;
|
||||||
|
const qreal b = pixel[2] / 255.0;
|
||||||
|
totalLuminance += std::sqrt(0.299 * r * r + 0.587 * g * g + 0.114 * b * b);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 dominantColour = 0;
|
||||||
|
int maxCount = 0;
|
||||||
|
for (const auto& [colour, colourCount] : colours) {
|
||||||
|
if (promise.isCanceled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colourCount > maxCount) {
|
||||||
|
dominantColour = colour;
|
||||||
|
maxCount = colourCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.addResult(qMakePair(QColor((0xFFu << 24) | dominantColour), count == 0 ? 0.0 : totalLuminance / count));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ZShell
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtQuick/qquickitem.h>
|
||||||
|
#include <qfuture.h>
|
||||||
|
#include <qfuturewatcher.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
|
||||||
|
namespace ZShell {
|
||||||
|
|
||||||
|
class ImageAnalyser : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
|
||||||
|
Q_PROPERTY(QQuickItem* sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged)
|
||||||
|
Q_PROPERTY(int rescaleSize READ rescaleSize WRITE setRescaleSize NOTIFY rescaleSizeChanged)
|
||||||
|
Q_PROPERTY(QColor dominantColour READ dominantColour NOTIFY dominantColourChanged)
|
||||||
|
Q_PROPERTY(qreal luminance READ luminance NOTIFY luminanceChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ImageAnalyser(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
[[nodiscard]] QString source() const;
|
||||||
|
void setSource(const QString& source);
|
||||||
|
|
||||||
|
[[nodiscard]] QQuickItem* sourceItem() const;
|
||||||
|
void setSourceItem(QQuickItem* sourceItem);
|
||||||
|
|
||||||
|
[[nodiscard]] int rescaleSize() const;
|
||||||
|
void setRescaleSize(int rescaleSize);
|
||||||
|
|
||||||
|
[[nodiscard]] QColor dominantColour() const;
|
||||||
|
[[nodiscard]] qreal luminance() const;
|
||||||
|
|
||||||
|
Q_INVOKABLE void requestUpdate();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sourceChanged();
|
||||||
|
void sourceItemChanged();
|
||||||
|
void rescaleSizeChanged();
|
||||||
|
void dominantColourChanged();
|
||||||
|
void luminanceChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
using AnalyseResult = QPair<QColor, qreal>;
|
||||||
|
|
||||||
|
QFutureWatcher<AnalyseResult>* const m_futureWatcher;
|
||||||
|
|
||||||
|
QString m_source;
|
||||||
|
QQuickItem* m_sourceItem;
|
||||||
|
int m_rescaleSize;
|
||||||
|
|
||||||
|
QColor m_dominantColour;
|
||||||
|
qreal m_luminance;
|
||||||
|
|
||||||
|
void update();
|
||||||
|
static void analyse(QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ZShell
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// Time.qml
|
|
||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"primary_paletteKeyColor": "#A56A24",
|
||||||
|
"secondary_paletteKeyColor": "#587C8E",
|
||||||
|
"tertiary_paletteKeyColor": "#3A8188",
|
||||||
|
"neutral_paletteKeyColor": "#6F7979",
|
||||||
|
"neutral_variant_paletteKeyColor": "#697A7B",
|
||||||
|
"background": "#0C1515",
|
||||||
|
"onBackground": "#DAE4E4",
|
||||||
|
"surface": "#0C1515",
|
||||||
|
"surfaceDim": "#0C1515",
|
||||||
|
"surfaceBright": "#313B3B",
|
||||||
|
"surfaceContainerLowest": "#071010",
|
||||||
|
"surfaceContainerLow": "#141D1E",
|
||||||
|
"surfaceContainer": "#182122",
|
||||||
|
"surfaceContainerHigh": "#222C2C",
|
||||||
|
"surfaceContainerHighest": "#2D3637",
|
||||||
|
"onSurface": "#DAE4E4",
|
||||||
|
"surfaceVariant": "#394A4A",
|
||||||
|
"onSurfaceVariant": "#B8CACA",
|
||||||
|
"inverseSurface": "#DAE4E4",
|
||||||
|
"inverseOnSurface": "#293232",
|
||||||
|
"outline": "#829494",
|
||||||
|
"outlineVariant": "#394A4A",
|
||||||
|
"shadow": "#000000",
|
||||||
|
"scrim": "#000000",
|
||||||
|
"surfaceTint": "#FFB86E",
|
||||||
|
"primary": "#FFB86E",
|
||||||
|
"onPrimary": "#492900",
|
||||||
|
"primaryContainer": "#693C00",
|
||||||
|
"onPrimaryContainer": "#FFDCBD",
|
||||||
|
"inversePrimary": "#88520A",
|
||||||
|
"secondary": "#A6CCE0",
|
||||||
|
"onSecondary": "#0A3545",
|
||||||
|
"secondaryContainer": "#284E5E",
|
||||||
|
"onSecondaryContainer": "#C2E8FD",
|
||||||
|
"tertiary": "#8CD2D9",
|
||||||
|
"onTertiary": "#00363B",
|
||||||
|
"tertiaryContainer": "#569BA2",
|
||||||
|
"onTertiaryContainer": "#000000",
|
||||||
|
"error": "#FFB4AB",
|
||||||
|
"onError": "#690005",
|
||||||
|
"errorContainer": "#93000A",
|
||||||
|
"onErrorContainer": "#FFDAD6",
|
||||||
|
"primaryFixed": "#FFDCBD",
|
||||||
|
"primaryFixedDim": "#FFB86E",
|
||||||
|
"onPrimaryFixed": "#2C1600",
|
||||||
|
"onPrimaryFixedVariant": "#693C00",
|
||||||
|
"secondaryFixed": "#C2E8FD",
|
||||||
|
"secondaryFixedDim": "#A6CCE0",
|
||||||
|
"onSecondaryFixed": "#001F2A",
|
||||||
|
"onSecondaryFixedVariant": "#264B5C",
|
||||||
|
"tertiaryFixed": "#A8EEF6",
|
||||||
|
"tertiaryFixedDim": "#8CD2D9",
|
||||||
|
"onTertiaryFixed": "#002022",
|
||||||
|
"onTertiaryFixedVariant": "#004F55"
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import json
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
from PIL import Image
|
||||||
|
from materialyoucolor.quantize import QuantizeCelebi
|
||||||
|
from materialyoucolor.score.score import Score
|
||||||
|
from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors
|
||||||
|
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot
|
||||||
|
from materialyoucolor.hct.hct import Hct
|
||||||
|
|
||||||
|
|
||||||
|
def generate_thumbnail(image_path, thumbnail_path, size=(128, 128)):
|
||||||
|
thumbnail_file = Path(thumbnail_path)
|
||||||
|
|
||||||
|
image = Image.open(image_path)
|
||||||
|
image = image.convert("RGB")
|
||||||
|
image.thumbnail(size, Image.NEAREST)
|
||||||
|
|
||||||
|
thumbnail_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
image.save(thumbnail_path, "JPEG")
|
||||||
|
|
||||||
|
|
||||||
|
def generate_color_scheme(thumbnail_path, output_path):
|
||||||
|
image = Image.open(thumbnail_path)
|
||||||
|
pixel_len = image.width * image.height
|
||||||
|
image_data = image.getdata()
|
||||||
|
|
||||||
|
quality = 1
|
||||||
|
pixel_array = [image_data[_] for _ in range(0, pixel_len, quality)]
|
||||||
|
|
||||||
|
result = QuantizeCelebi(pixel_array, 128)
|
||||||
|
score = Score.score(result)[0]
|
||||||
|
|
||||||
|
scheme = SchemeTonalSpot(
|
||||||
|
Hct.from_int(score),
|
||||||
|
True,
|
||||||
|
0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
color_dict = {}
|
||||||
|
for color in vars(MaterialDynamicColors).keys():
|
||||||
|
color_name = getattr(MaterialDynamicColors, color)
|
||||||
|
if hasattr(color_name, "get_hct"):
|
||||||
|
color_int = color_name.get_hct(scheme).to_int()
|
||||||
|
color_dict[color] = int_to_hex(color_int)
|
||||||
|
|
||||||
|
output_dict = {
|
||||||
|
"name": "dynamic",
|
||||||
|
"flavour": "default",
|
||||||
|
"mode": "dark",
|
||||||
|
"variant": "tonalspot",
|
||||||
|
"colors": color_dict
|
||||||
|
}
|
||||||
|
|
||||||
|
output_file = Path(output_path)
|
||||||
|
output_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
with open(output_file, "w") as f:
|
||||||
|
json.dump(output_dict, f, indent=4)
|
||||||
|
|
||||||
|
|
||||||
|
def int_to_hex(argb_int):
|
||||||
|
return "#{:06X}".format(argb_int & 0xFFFFFF)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Generate color scheme from wallpaper image"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--path",
|
||||||
|
required=True,
|
||||||
|
help="Path to the wallpaper image"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--output",
|
||||||
|
required=True,
|
||||||
|
help="Path to save the color scheme JSON file"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--thumbnail",
|
||||||
|
required=True,
|
||||||
|
help="Path to save the thumbnail image"
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
generate_thumbnail(args.path, str(args.thumbnail))
|
||||||
|
generate_color_scheme(str(args.thumbnail), args.output)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
exit(main())
|
||||||
Reference in New Issue
Block a user