dynamic color scheme progress
This commit is contained in:
@@ -3,3 +3,4 @@
|
||||
.qmlls.ini
|
||||
build/
|
||||
compile_commands.json
|
||||
testpython
|
||||
|
||||
@@ -39,9 +39,13 @@ Scope {
|
||||
Rectangle {
|
||||
id: backgroundRect
|
||||
anchors.fill: parent
|
||||
color: Config.baseBgColor
|
||||
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surface : Config.baseBgColor
|
||||
radius: 0
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 5
|
||||
@@ -92,11 +96,17 @@ Scope {
|
||||
|
||||
Text {
|
||||
id: notificationCenterIcon
|
||||
property color iconColor: Config.useDynamicColors ? DynamicColors.palette.m3tertiaryFixed : "white"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: HasNotifications.hasNotifications ? "\uf4fe" : "\ue7f4"
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pixelSize: 20
|
||||
color: "white"
|
||||
color: iconColor
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import QtQuick
|
||||
import qs.Config
|
||||
import qs.Modules
|
||||
|
||||
Text {
|
||||
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 gpuType: adapter.gpuType
|
||||
property alias background: adapter.background
|
||||
property alias useDynamicColors: adapter.useDynamicColors
|
||||
|
||||
FileView {
|
||||
id: root
|
||||
@@ -42,6 +43,7 @@ Singleton {
|
||||
property Colors colors: Colors {}
|
||||
property string gpuType: ""
|
||||
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.Widgets
|
||||
import qs.Modules
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -12,6 +13,7 @@ Item {
|
||||
implicitHeight: 34
|
||||
|
||||
property bool expanded: false
|
||||
property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3tertiaryFixed : "#ffffff"
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
@@ -34,8 +36,11 @@ Item {
|
||||
anchors.right: parent.right
|
||||
height: 22
|
||||
radius: height / 2
|
||||
color: "#40000000"
|
||||
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surfaceContainer : "#40000000"
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
|
||||
// Background circle
|
||||
Rectangle {
|
||||
@@ -66,8 +71,11 @@ Item {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pixelSize: 18
|
||||
text: "\ue050" // volume_up icon
|
||||
color: "#ffffff"
|
||||
text: "\ue050" // volumeUp icon
|
||||
color: root.textColor
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -87,7 +95,7 @@ Item {
|
||||
|
||||
implicitWidth: parent.width * ( Pipewire.defaultAudioSink?.audio.volume ?? 0 )
|
||||
radius: parent.radius
|
||||
color: "#ffffff"
|
||||
color: root.textColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -97,7 +105,7 @@ Item {
|
||||
width: sinkVolumeMouseArea.pressed ? 25 : 12
|
||||
height: sinkVolumeMouseArea.pressed ? 25 : 12
|
||||
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.width: 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -171,7 +179,11 @@ Item {
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pixelSize: 18
|
||||
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 {
|
||||
@@ -191,7 +203,11 @@ Item {
|
||||
|
||||
implicitWidth: parent.width * ( Pipewire.defaultAudioSource?.audio.volume ?? 0 )
|
||||
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 {
|
||||
@@ -201,7 +217,7 @@ Item {
|
||||
width: sourceVolumeMouseArea.pressed ? 25 : 12
|
||||
height: sourceVolumeMouseArea.pressed ? 25 : 12
|
||||
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.width: 2
|
||||
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 qs.Helpers
|
||||
import qs.Config
|
||||
import qs.Paths
|
||||
|
||||
TextField {
|
||||
id: root
|
||||
@@ -84,6 +85,8 @@ TextField {
|
||||
launcherWindow.visible = false;
|
||||
} else if ( wallpaperPickerLoader.active ) {
|
||||
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 ) {
|
||||
Wallust.generateColors(WallpaperPath.currentWallpaperPath);
|
||||
}
|
||||
|
||||
+13
-2
@@ -15,6 +15,8 @@ Item {
|
||||
implicitWidth: resourceRowLayout.x < 0 ? 0 : resourceRowLayout.implicitWidth
|
||||
implicitHeight: 22
|
||||
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 {
|
||||
NumberAnimation {
|
||||
@@ -55,9 +57,14 @@ Item {
|
||||
|
||||
ShapePath {
|
||||
strokeWidth: 0
|
||||
fillColor: root.warning ? Config.accentColor.accents.warning : Config.accentColor.accents.primary
|
||||
fillColor: root.usageColor
|
||||
startX: backgroundCircle.width / 2
|
||||
startY: backgroundCircle.height / 2
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {}
|
||||
}
|
||||
|
||||
PathLine {
|
||||
x: backgroundCircle.width / 2
|
||||
y: 0 + ( 1 / 2 )
|
||||
@@ -80,10 +87,14 @@ Item {
|
||||
|
||||
ShapePath {
|
||||
strokeWidth: 1
|
||||
strokeColor: root.warning ? Config.accentColor.accents.warningAlt : Config.accentColor.accents.primaryAlt
|
||||
strokeColor: root.borderColor
|
||||
fillColor: "transparent"
|
||||
capStyle: ShapePath.FlatCap
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {}
|
||||
}
|
||||
|
||||
PathAngleArc {
|
||||
centerX: backgroundCircle.width / 2
|
||||
centerY: backgroundCircle.height / 2
|
||||
|
||||
+18
-5
@@ -10,6 +10,7 @@ Item {
|
||||
id: root
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
|
||||
implicitHeight: 34
|
||||
property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3tertiaryFixed : "#ffffff"
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
@@ -18,7 +19,7 @@ Item {
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
implicitHeight: 22
|
||||
color: "#40000000"
|
||||
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surfaceContainer : "#40000000"
|
||||
radius: height / 2
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
@@ -33,7 +34,10 @@ Item {
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pixelSize: 18
|
||||
text: "\uf7a3"
|
||||
color: "#ffffff"
|
||||
color: root.textColor
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
|
||||
Resource {
|
||||
@@ -46,7 +50,10 @@ Item {
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pixelSize: 18
|
||||
text: "\ue322"
|
||||
color: "#ffffff"
|
||||
color: root.textColor
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
|
||||
Resource {
|
||||
@@ -59,7 +66,10 @@ Item {
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pixelSize: 16
|
||||
text: "\ue30f"
|
||||
color: "#ffffff"
|
||||
color: root.textColor
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
|
||||
Resource {
|
||||
@@ -71,7 +81,10 @@ Item {
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pixelSize: 18
|
||||
text: "\ue30d"
|
||||
color: "#ffffff"
|
||||
color: root.textColor
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
|
||||
Resource {
|
||||
|
||||
@@ -2,8 +2,11 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.SystemTray
|
||||
import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
import qs.Modules
|
||||
import qs.Config
|
||||
import Caelestia
|
||||
import QtQuick.Effects
|
||||
|
||||
MouseArea {
|
||||
id: root
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Modules
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property int countUpdates
|
||||
implicitWidth: contentRow.childrenRect.width + 10
|
||||
implicitHeight: 22
|
||||
property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3tertiaryFixed : "#ffffff"
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: height / 2
|
||||
color: "#40000000"
|
||||
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surfaceContainer : "#40000000"
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@@ -28,7 +33,10 @@ Item {
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pixelSize: 18
|
||||
text: "\uf569"
|
||||
color: "#ffffff"
|
||||
color: root.textColor
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
@@ -40,7 +48,10 @@ Item {
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
text: textMetrics.text
|
||||
color: "white"
|
||||
color: root.textColor
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-2
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Hyprland
|
||||
import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -11,6 +12,7 @@ Item {
|
||||
clip: true
|
||||
|
||||
property bool showFirst: true
|
||||
property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3primary : "white"
|
||||
|
||||
Component.onCompleted: {
|
||||
Hyprland.rawEvent.connect(( event ) => {
|
||||
@@ -37,7 +39,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
text: root.currentTitle
|
||||
color: "white"
|
||||
color: root.textColor
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: 16
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
@@ -46,13 +48,17 @@ Item {
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: titleText2
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
color: "white"
|
||||
color: root.textColor
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: 16
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
@@ -61,5 +67,9 @@ Item {
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-11
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.Config
|
||||
@@ -26,7 +27,7 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
color: "#40000000"
|
||||
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surfaceContainer : "#40000000"
|
||||
radius: height / 2
|
||||
|
||||
Behavior on implicitWidth {
|
||||
@@ -35,7 +36,11 @@ Rectangle {
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: workspacesRow
|
||||
anchors.left: parent.left
|
||||
@@ -47,15 +52,16 @@ Rectangle {
|
||||
model: Hyprland.workspaces
|
||||
|
||||
Rectangle {
|
||||
id: workspaceIndicator
|
||||
required property var modelData
|
||||
|
||||
width: 16
|
||||
height: 16
|
||||
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
|
||||
|
||||
visible: root.shouldShow( modelData.monitor )
|
||||
@@ -90,13 +96,13 @@ Rectangle {
|
||||
duration: 200
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: modelData.id
|
||||
font.pixelSize: 10
|
||||
font.family: "Rubik"
|
||||
color: modelData.id === Hyprland.focusedWorkspace.id ? Config.workspaceWidget.textColor : Config.workspaceWidget.inactiveTextColor
|
||||
}
|
||||
// Text {
|
||||
// anchors.centerIn: parent
|
||||
// text: modelData.id
|
||||
// font.pixelSize: 10
|
||||
// font.family: "Rubik"
|
||||
// color: modelData.id === Hyprland.focusedWorkspace.id ? Config.workspaceWidget.textColor : Config.workspaceWidget.inactiveTextColor
|
||||
// }
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -33,6 +33,7 @@ qml_module(ZShell
|
||||
SOURCES
|
||||
writefile.hpp writefile.cpp
|
||||
appdb.hpp appdb.cpp
|
||||
imageanalyser.hpp imageanalyser.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
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
|
||||
@@ -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