internal: better colourisation

This commit is contained in:
2 * r + 2 * t 2025-08-14 16:25:46 +10:00
parent 38b2ef0061
commit 04ce9ccbb4
8 changed files with 100 additions and 42 deletions

View file

@ -0,0 +1,83 @@
pragma ComponentBehavior: Bound
import Quickshell.Widgets
import QtQuick
IconImage {
id: root
required property color colour
property color sourceColour
property url lastSource
asynchronous: true
layer.enabled: true
layer.effect: Colouriser {
sourceColor: root.sourceColour
colorizationColor: root.colour
}
layer.onEnabledChanged: {
if (layer.enabled)
canvas.requestPaint();
}
onStatusChanged: {
if (layer.enabled && status === Image.Ready)
canvas.requestPaint();
}
Canvas {
id: canvas
property int retryCount
implicitWidth: 32
implicitHeight: 32
visible: false
onPaint: {
if (!root.layer.enabled)
return;
const ctx = getContext("2d");
ctx.reset();
ctx.drawImage(root.backer, 0, 0, width, height);
const colours = {} as Object;
const data = ctx.getImageData(0, 0, width, height).data;
for (let i = 0; i < data.length; i += 4) {
if (data[i + 3] === 0)
continue;
const c = `${data[i]},${data[i + 1]},${data[i + 2]}`;
if (colours.hasOwnProperty(c))
colours[c]++;
else
colours[c] = 1;
}
// Canvas is empty, try again next frame
if (retryCount < 5 && !Object.keys(colours).length) {
retryCount++;
Qt.callLater(() => requestPaint());
return;
}
let max = 0;
let maxColour = "0,0,0";
for (const [colour, occurences] of Object.entries(colours)) {
if (occurences > max) {
max = occurences;
maxColour = colour;
}
}
const [r, g, b] = maxColour.split(",");
root.sourceColour = Qt.rgba(r / 255, g / 255, b / 255, 1);
retryCount = 0;
}
}
}

View file

@ -3,8 +3,10 @@ import QtQuick
import QtQuick.Effects
MultiEffect {
property color sourceColor: "black"
colorization: 1
brightness: colorizationColor.hslLightness
brightness: 1 - sourceColor.hslLightness
Behavior on colorizationColor {
ColorAnimation {

View file

@ -1,15 +1,10 @@
import qs.components.effects
import qs.services
import qs.utils
import qs.config
import Quickshell.Widgets
import qs.utils
IconImage {
ColouredIcon {
source: SysInfo.osLogo
implicitSize: Appearance.font.size.large * 1.2
layer.enabled: true
layer.effect: Colouriser {
colorizationColor: Colours.palette.m3tertiary
}
colour: Colours.palette.m3tertiary
}

View file

@ -51,6 +51,7 @@ StyledRect {
Colouriser {
source: root.mask
sourceColor: Colours.palette.m3onSurface
colorizationColor: Colours.palette.m3onPrimary
x: 0

View file

@ -5,7 +5,6 @@ import qs.services
import qs.config
import qs.utils
import Quickshell
import Quickshell.Widgets
import QtQuick
Row {
@ -119,7 +118,7 @@ Row {
implicitWidth: icon.implicitWidth + text.width + text.anchors.leftMargin
implicitHeight: Math.max(icon.implicitHeight, text.implicitHeight)
IconImage {
ColouredIcon {
id: icon
anchors.left: parent.left
@ -127,11 +126,7 @@ Row {
source: SysInfo.osLogo
implicitSize: Math.floor(Appearance.font.size.normal * 1.34)
layer.enabled: true
layer.effect: Colouriser {
colorizationColor: Colours.palette.m3primary
}
colour: Colours.palette.m3primary
}
StyledText {

View file

@ -1,12 +1,10 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.components.widgets
import qs.components.effects
import qs.services
import qs.config
import qs.utils
import Quickshell.Widgets
import Quickshell.Services.UPower
import QtQuick
import QtQuick.Layouts
@ -149,15 +147,11 @@ ColumnLayout {
visible: active
}
component OsLogo: IconImage {
component OsLogo: ColouredIcon {
source: SysInfo.osLogo
implicitSize: height
asynchronous: true
colour: Colours.palette.m3primary
layer.enabled: Config.lock.recolourLogo || SysInfo.isDefaultLogo
layer.effect: Colouriser {
colorizationColor: Colours.palette.m3primary
}
}
component FetchText: MonoText {

View file

@ -62,15 +62,11 @@ StyledRect {
Component {
id: appIconComp
IconImage {
ColouredIcon {
implicitSize: Math.round(Config.notifs.sizes.image * 0.6)
source: Quickshell.iconPath(root.appIcon)
asynchronous: true
colour: root.urgency === "critical" ? Colours.palette.m3onError : root.urgency === "low" ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
layer.enabled: root.appIcon.endsWith("symbolic")
layer.effect: Colouriser {
colorizationColor: root.urgency === "critical" ? Colours.palette.m3onError : root.urgency === "low" ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
}
}
}
@ -109,16 +105,12 @@ StyledRect {
color: root.urgency === "critical" ? Colours.palette.m3error : root.urgency === "low" ? Colours.palette.m3surfaceContainerHighest : Colours.palette.m3secondaryContainer
radius: Appearance.rounding.full
IconImage {
ColouredIcon {
anchors.centerIn: parent
implicitSize: Math.round(Config.notifs.sizes.badge * 0.6)
source: Quickshell.iconPath(root.appIcon)
asynchronous: true
colour: root.urgency === "critical" ? Colours.palette.m3onError : root.urgency === "low" ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
layer.enabled: root.appIcon.endsWith("symbolic")
layer.effect: Colouriser {
colorizationColor: root.urgency === "critical" ? Colours.palette.m3onError : root.urgency === "low" ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
}
}
}
}

View file

@ -162,15 +162,11 @@ StyledRect {
width: Math.round(parent.width * 0.6)
height: Math.round(parent.width * 0.6)
sourceComponent: IconImage {
sourceComponent: ColouredIcon {
anchors.fill: parent
source: Quickshell.iconPath(root.modelData.appIcon)
asynchronous: true
colour: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? Colours.palette.m3onSurface : Colours.palette.m3onTertiaryContainer
layer.enabled: root.modelData.appIcon.endsWith("symbolic")
layer.effect: Colouriser {
colorizationColor: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? Colours.palette.m3onSurface : Colours.palette.m3onTertiaryContainer
}
}
}