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 import QtQuick.Effects
MultiEffect { MultiEffect {
property color sourceColor: "black"
colorization: 1 colorization: 1
brightness: colorizationColor.hslLightness brightness: 1 - sourceColor.hslLightness
Behavior on colorizationColor { Behavior on colorizationColor {
ColorAnimation { ColorAnimation {

View file

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

View file

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

View file

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

View file

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

View file

@ -62,15 +62,11 @@ StyledRect {
Component { Component {
id: appIconComp id: appIconComp
IconImage { ColouredIcon {
implicitSize: Math.round(Config.notifs.sizes.image * 0.6) implicitSize: Math.round(Config.notifs.sizes.image * 0.6)
source: Quickshell.iconPath(root.appIcon) 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.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 color: root.urgency === "critical" ? Colours.palette.m3error : root.urgency === "low" ? Colours.palette.m3surfaceContainerHighest : Colours.palette.m3secondaryContainer
radius: Appearance.rounding.full radius: Appearance.rounding.full
IconImage { ColouredIcon {
anchors.centerIn: parent anchors.centerIn: parent
implicitSize: Math.round(Config.notifs.sizes.badge * 0.6) implicitSize: Math.round(Config.notifs.sizes.badge * 0.6)
source: Quickshell.iconPath(root.appIcon) 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.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) width: Math.round(parent.width * 0.6)
height: Math.round(parent.width * 0.6) height: Math.round(parent.width * 0.6)
sourceComponent: IconImage { sourceComponent: ColouredIcon {
anchors.fill: parent anchors.fill: parent
source: Quickshell.iconPath(root.modelData.appIcon) 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.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
}
} }
} }