mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 23:09:26 -05:00
adjust media control colors
This commit is contained in:
@@ -14,6 +14,57 @@ Singleton {
|
||||
property QtObject sizes
|
||||
property string syntaxHighlightingTheme
|
||||
|
||||
function colorWithHueOf(color1, color2) {
|
||||
// Convert both colors to HSV
|
||||
var c1 = Qt.color(color1);
|
||||
var c2 = Qt.color(color2);
|
||||
|
||||
// Helper to convert RGB to HSV
|
||||
function rgb2hsv(c) {
|
||||
var r = c.r, g = c.g, b = c.b;
|
||||
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||
var h, s, v = max;
|
||||
var d = max - min;
|
||||
s = max === 0 ? 0 : d / max;
|
||||
if (max === min) {
|
||||
h = 0;
|
||||
} else {
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
return {h: h, s: s, v: v, a: c.a};
|
||||
}
|
||||
|
||||
// Helper to convert HSV to RGB
|
||||
function hsv2rgb(h, s, v, a) {
|
||||
var r, g, b;
|
||||
var i = Math.floor(h * 6);
|
||||
var f = h * 6 - i;
|
||||
var p = v * (1 - s);
|
||||
var q = v * (1 - f * s);
|
||||
var t = v * (1 - (1 - f) * s);
|
||||
switch(i % 6){
|
||||
case 0: r = v, g = t, b = p; break;
|
||||
case 1: r = q, g = v, b = p; break;
|
||||
case 2: r = p, g = v, b = t; break;
|
||||
case 3: r = p, g = q, b = v; break;
|
||||
case 4: r = t, g = p, b = v; break;
|
||||
case 5: r = v, g = p, b = q; break;
|
||||
}
|
||||
return Qt.rgba(r, g, b, a);
|
||||
}
|
||||
|
||||
var hsv1 = rgb2hsv(c1);
|
||||
var hsv2 = rgb2hsv(c2);
|
||||
|
||||
// Use hue from color2, saturation/value/alpha from color1
|
||||
return hsv2rgb(hsv2.h, hsv1.s, hsv1.v, hsv1.a);
|
||||
}
|
||||
|
||||
function mix(color1, color2, percentage) {
|
||||
var c1 = Qt.color(color1);
|
||||
var c2 = Qt.color(color2);
|
||||
|
||||
@@ -13,6 +13,8 @@ ProgressBar {
|
||||
property real valueBarWidth: 120
|
||||
property real valueBarHeight: 4
|
||||
property real valueBarGap: 4
|
||||
property color highlightColor: Appearance.m3colors.m3primary
|
||||
property color trackColor: Appearance.m3colors.m3secondaryContainer
|
||||
|
||||
Behavior on value {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
@@ -34,21 +36,21 @@ ProgressBar {
|
||||
width: root.visualPosition * parent.width
|
||||
height: parent.height
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.m3colors.m3primary
|
||||
color: root.highlightColor
|
||||
}
|
||||
Rectangle { // Right remaining part fill
|
||||
anchors.right: parent.right
|
||||
width: (1 - root.visualPosition) * parent.width - valueBarGap
|
||||
height: parent.height
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.m3colors.m3secondaryContainer
|
||||
color: root.trackColor
|
||||
}
|
||||
Rectangle { // Stop point
|
||||
anchors.right: parent.right
|
||||
width: valueBarGap
|
||||
height: valueBarGap
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.m3colors.m3primary
|
||||
color: root.highlightColor
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,17 +27,17 @@ Scope {
|
||||
property real artRounding: Appearance.rounding.verysmall
|
||||
property string baseCoverArtDir: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/coverart`)
|
||||
|
||||
// property bool hasPlasmaIntegration: true
|
||||
property bool hasPlasmaIntegration: false
|
||||
function isRealPlayer(player) {
|
||||
// return true
|
||||
return (
|
||||
// Remove unecessary native buses from browsers if there's plasma integration
|
||||
// !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.firefox')) &&
|
||||
// !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) &&
|
||||
!(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.firefox')) &&
|
||||
!(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.chromium')) &&
|
||||
// playerctld just copies other buses and we don't need duplicates
|
||||
!player.dbusName?.startsWith('org.mpris.MediaPlayer2.playerctld') &&
|
||||
// Non-instance mpd bus
|
||||
!(player.dbusName?.endsWith('.mpd') && !player.busName.endsWith('MediaPlayer2.mpd'))
|
||||
!(player.dbusName?.endsWith('.mpd') && !player.dbusName.endsWith('MediaPlayer2.mpd'))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -82,9 +82,8 @@ Scope {
|
||||
spacing: -Appearance.sizes.elevationMargin // Shadow overlap okay
|
||||
|
||||
Repeater {
|
||||
model: {
|
||||
// console.log(JSON.stringify(Mpris.players, null, 2))
|
||||
return Mpris.players.values.filter(player => isRealPlayer(player))
|
||||
model: ScriptModel {
|
||||
values: Mpris.players.values.filter(player => isRealPlayer(player))
|
||||
}
|
||||
delegate: PlayerControl {
|
||||
required property MprisPlayer modelData
|
||||
|
||||
@@ -18,39 +18,51 @@ Item { // Player instance
|
||||
required property MprisPlayer player
|
||||
// property var artUrl: player?.metadata["xesam:url"] || player?.metadata["mpris:artUrl"] || player?.trackArtUrl
|
||||
property var artUrl: player?.trackArtUrl
|
||||
property string localArt
|
||||
property color artDominantColor: "#00000000"
|
||||
property color artDominantColor: Appearance.m3colors.m3primaryFixed
|
||||
|
||||
implicitWidth: widgetWidth
|
||||
implicitHeight: widgetHeight
|
||||
|
||||
Timer { // Force update for prevision
|
||||
running: playerController.player?.playbackState == MprisPlaybackState.Playing
|
||||
interval: 1000
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
playerController.player.positionChanged()
|
||||
}
|
||||
}
|
||||
|
||||
onArtUrlChanged: {
|
||||
if (playerController.artUrl.length == 0) return;
|
||||
colorQuantizer.targetFile = playerController.artUrl // Yes this binding break is intentional
|
||||
colorQuantizer.running = true
|
||||
}
|
||||
|
||||
Process { // Average Color Runner
|
||||
id: colorQuantizer
|
||||
command: [ "sh", "-c", `magick ${playerController.player.trackArtUrl} -scale 1x1\\! -format '%[fx:int(255*r+.5)],%[fx:int(255*g+.5)],%[fx:int(255*b+.5)]' info: | sed 's/,/\\n/g' | xargs -L 1 printf '%02x' ; echo` ]
|
||||
property string targetFile: playerController.artUrl
|
||||
command: [ "sh", "-c", `magick '${targetFile}' -scale 1x1\\! -format '%[fx:int(255*r+.5)],%[fx:int(255*g+.5)],%[fx:int(255*b+.5)]' info: | sed 's/,/\\n/g' | xargs -L 1 printf '%02x' ; echo` ]
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
// console.log("Color quantizer output:", data)
|
||||
playerController.artDominantColor = "#" + data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property QtObject blendedColors: QtObject {
|
||||
property color colLayer0: Appearance.mix(Appearance.colors.colLayer0, artDominantColor, 0.7)
|
||||
property color colLayer0: Appearance.mix(Appearance.colors.colLayer0, artDominantColor, 0.6)
|
||||
property color colLayer1: Appearance.mix(Appearance.colors.colLayer1, artDominantColor, 0.5)
|
||||
property color colOnLayer0: Appearance.mix(Appearance.colors.colOnLayer0, artDominantColor, 0.7)
|
||||
property color colOnLayer1: Appearance.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.5)
|
||||
property color colSubtext: Appearance.mix(Appearance.colors.colSubtext, artDominantColor, 0.5)
|
||||
property color colPrimary: Appearance.mix(Appearance.m3colors.m3primary, artDominantColor, 0.3)
|
||||
property color colPrimaryHover: Appearance.mix(Appearance.colors.colPrimaryHover, artDominantColor, 0.3)
|
||||
property color colPrimaryActive: Appearance.mix(Appearance.colors.colPrimaryActive, artDominantColor, 0.3)
|
||||
property color colSecondaryContainer: Appearance.mix(Appearance.m3colors.m3secondaryContainer, artDominantColor, 0.5)
|
||||
property color colPrimary: Appearance.mix(Appearance.colorWithHueOf(Appearance.m3colors.m3primary, artDominantColor), artDominantColor, 0.5)
|
||||
property color colPrimaryHover: Appearance.mix(Appearance.colorWithHueOf(Appearance.colors.colPrimaryHover, artDominantColor), artDominantColor, 0.3)
|
||||
property color colPrimaryActive: Appearance.mix(Appearance.colorWithHueOf(Appearance.colors.colPrimaryActive, artDominantColor), artDominantColor, 0.3)
|
||||
property color colSecondaryContainer: Appearance.mix(Appearance.m3colors.m3secondaryContainer, artDominantColor, 0.3)
|
||||
property color colSecondaryContainerHover: Appearance.mix(Appearance.colors.colSecondaryContainerHover, artDominantColor, 0.3)
|
||||
property color colSecondaryContainerActive: Appearance.mix(Appearance.colors.colSecondaryContainerActive, artDominantColor, 0.3)
|
||||
property color colOnPrimary: Appearance.mix(Appearance.colors.colOnPrimary, artDominantColor, 0.5)
|
||||
property color colOnPrimary: Appearance.mix(Appearance.colorWithHueOf(Appearance.m3colors.m3onPrimary, artDominantColor), artDominantColor, 0.5)
|
||||
property color colOnSecondaryContainer: Appearance.mix(Appearance.m3colors.m3onSecondaryContainer, artDominantColor, 0.2)
|
||||
|
||||
}
|
||||
@@ -62,6 +74,24 @@ Item { // Player instance
|
||||
color: blendedColors.colLayer0
|
||||
radius: root.popupRounding
|
||||
|
||||
LinearGradient {
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: background.width
|
||||
height: background.height
|
||||
radius: root.popupRounding
|
||||
}
|
||||
}
|
||||
start: Qt.point(0, 0)
|
||||
end: Qt.point(background.width, background.height)
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: Appearance.transparentize(artDominantColor, 0.6) }
|
||||
GradientStop { position: 0.4; color: Appearance.transparentize(artDominantColor, 0.8) }
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.contentPadding
|
||||
@@ -128,35 +158,31 @@ Item { // Player instance
|
||||
StyledText {
|
||||
id: trackTime
|
||||
anchors.bottom: slider.top
|
||||
anchors.bottomMargin: -4
|
||||
anchors.bottomMargin: 5
|
||||
anchors.left: parent.left
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: blendedColors.colSubtext
|
||||
elide: Text.ElideRight
|
||||
text: `${StringUtils.friendlyTimeForSeconds(playerController.player?.position)} / ${StringUtils.friendlyTimeForSeconds(playerController.player?.length)}`
|
||||
}
|
||||
StyledSlider {
|
||||
StyledProgressBar {
|
||||
id: slider
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottomMargin: -8
|
||||
bottomMargin: 5
|
||||
}
|
||||
highlightColor: blendedColors.colPrimary
|
||||
trackColor: blendedColors.colSecondaryContainer
|
||||
handleColor: blendedColors.colOnSecondaryContainer
|
||||
scale: 0.6
|
||||
value: playerController.player?.position / playerController.player?.length
|
||||
onMoved: playerController.player.position = value * playerController.player.length
|
||||
tooltipContent: StringUtils.friendlyTimeForSeconds(playerController.player?.position)
|
||||
}
|
||||
|
||||
Button {
|
||||
id: playPauseButton
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: slider.top
|
||||
anchors.bottomMargin: -1
|
||||
anchors.bottomMargin: 20
|
||||
implicitWidth: 44
|
||||
implicitHeight: 44
|
||||
onClicked: playerController.player.togglePlaying();
|
||||
@@ -182,7 +208,7 @@ Item { // Player instance
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
fill: 1
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: playerController.player?.isPlaying ? Appearance.m3colors.m3onPrimary : Appearance.m3colors.m3onSecondaryContainer
|
||||
color: playerController.player?.isPlaying ? blendedColors.colOnPrimary : blendedColors.colOnSecondaryContainer
|
||||
text: playerController.player?.isPlaying ? "pause" : "play_arrow"
|
||||
|
||||
Behavior on color {
|
||||
|
||||
Reference in New Issue
Block a user