dynamic color scheme progress

This commit is contained in:
Zacharias-Brohn
2025-11-22 20:59:51 +01:00
parent d0db9a14d7
commit dc5273ab83
11 changed files with 508 additions and 75 deletions
+22 -2
View File
@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Wayland
import qs.Modules import qs.Modules
import qs.Config import qs.Config
import qs.Helpers import qs.Helpers
@@ -16,6 +17,20 @@ Scope {
property bool trayMenuVisible: false property bool trayMenuVisible: false
screen: modelData screen: modelData
property var root: Quickshell.shellDir property var root: Quickshell.shellDir
WlrLayershell.exclusionMode: ExclusionMode.Ignore
PanelWindow {
id: wrapper
screen: bar.screen
WlrLayershell.layer: WlrLayer.Bottom
anchors {
left: true
right: true
top: true
}
color: "transparent"
implicitHeight: 34
}
NotificationCenter { NotificationCenter {
bar: bar bar: bar
@@ -31,14 +46,19 @@ Scope {
top: true top: true
left: true left: true
right: true right: true
bottom: true
} }
implicitHeight: 34 mask: Region { item: backgroundRect }
color: "transparent" color: "transparent"
Rectangle { Rectangle {
id: backgroundRect id: backgroundRect
anchors.fill: parent anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
implicitHeight: 34
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surface : Config.baseBgColor color: Config.useDynamicColors ? DynamicColors.tPalette.m3surface : Config.baseBgColor
radius: 0 radius: 0
+13
View File
@@ -0,0 +1,13 @@
import Quickshell.Widgets
import QtQuick
import qs.Modules
ClippingRectangle {
id: root
color: "transparent"
Behavior on color {
CAnim {}
}
}
+19
View File
@@ -0,0 +1,19 @@
import QtQuick
MouseArea {
property int scrollAccumulatedY: 0
function onWheel(event: WheelEvent): void {
}
onWheel: event => {
if (Math.sign(event.angleDelta.y) !== Math.sign(scrollAccumulatedY))
scrollAccumulatedY = 0;
scrollAccumulatedY += event.angleDelta.y;
if (Math.abs(scrollAccumulatedY) >= 120) {
onWheel(event);
scrollAccumulatedY = 0;
}
}
}
+56
View File
@@ -0,0 +1,56 @@
import QtQuick
import QtQuick.Templates
import qs.Config
import qs.Modules
RadioButton {
id: root
font.pointSize: 12
implicitWidth: implicitIndicatorWidth + implicitContentWidth + contentItem.anchors.leftMargin
implicitHeight: Math.max(implicitIndicatorHeight, implicitContentHeight)
indicator: Rectangle {
id: outerCircle
implicitWidth: 20
implicitHeight: 20
radius: 1000
color: "transparent"
border.color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
border.width: 2
anchors.verticalCenter: parent.verticalCenter
StateLayer {
anchors.margins: -7
color: root.checked ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3primary
z: -1
function onClicked(): void {
root.click();
}
}
CustomRect {
anchors.centerIn: parent
implicitWidth: 8
implicitHeight: 8
radius: 1000
color: Qt.alpha(DynamicColors.palette.m3primary, root.checked ? 1 : 0)
}
Behavior on border.color {
CAnim {}
}
}
contentItem: CustomText {
text: root.text
font.pointSize: root.font.pointSize
anchors.verticalCenter: parent.verticalCenter
anchors.left: outerCircle.right
anchors.leftMargin: 10
}
}
+12
View File
@@ -0,0 +1,12 @@
import QtQuick
import qs.Modules
Rectangle {
id: root
color: "transparent"
Behavior on color {
CAnim {}
}
}
+56
View File
@@ -0,0 +1,56 @@
import QtQuick
import QtQuick.Templates
import qs.Config
import qs.Modules
Slider {
id: root
background: Item {
CustomRect {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.topMargin: root.implicitHeight / 3
anchors.bottomMargin: root.implicitHeight / 3
implicitWidth: root.handle.x - root.implicitHeight / 6
color: DynamicColors.palette.m3primary
radius: 1000
topRightRadius: root.implicitHeight / 15
bottomRightRadius: root.implicitHeight / 15
}
CustomRect {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.topMargin: root.implicitHeight / 3
anchors.bottomMargin: root.implicitHeight / 3
implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 6
color: DynamicColors.tPalette.m3surfaceContainer
radius: 1000
topLeftRadius: root.implicitHeight / 15
bottomLeftRadius: root.implicitHeight / 15
}
}
handle: CustomRect {
x: root.visualPosition * root.availableWidth - implicitWidth / 2
implicitWidth: root.implicitHeight / 4.5
implicitHeight: root.implicitHeight
color: DynamicColors.palette.m3primary
radius: 1000
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: Qt.PointingHandCursor
}
}
}
+48
View File
@@ -0,0 +1,48 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Config
import qs.Modules
Text {
id: root
property bool animate: false
property string animateProp: "scale"
property real animateFrom: 0
property real animateTo: 1
property int animateDuration: 400
renderType: Text.NativeRendering
textFormat: Text.PlainText
color: DynamicColors.palette.m3onSurface
font.family: "Rubik"
font.pointSize: 12
Behavior on color {
CAnim {}
}
Behavior on text {
enabled: root.animate
SequentialAnimation {
Anim {
to: root.animateFrom
easing.bezierCurve: MaterialEasing.standardAccel
}
PropertyAction {}
Anim {
to: root.animateTo
easing.bezierCurve: MaterialEasing.standardDecel
}
}
}
component Anim: NumberAnimation {
target: root
property: root.animateProp
duration: root.animateDuration / 2
easing.type: Easing.BezierSpline
}
}
+94
View File
@@ -0,0 +1,94 @@
import qs.Config
import qs.Modules
import QtQuick
MouseArea {
id: root
property bool disabled
property color color: DynamicColors.palette.m3onSurface
property real radius: parent?.radius ?? 0
property alias rect: hoverLayer
function onClicked(): void {
}
anchors.fill: parent
enabled: !disabled
cursorShape: disabled ? undefined : Qt.PointingHandCursor
hoverEnabled: true
onPressed: event => {
if (disabled)
return;
rippleAnim.x = event.x;
rippleAnim.y = event.y;
const dist = (ox, oy) => ox * ox + oy * oy;
rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y), dist(event.x, height - event.y), dist(width - event.x, event.y), dist(width - event.x, height - event.y)));
rippleAnim.restart();
}
onClicked: event => !disabled && onClicked(event)
SequentialAnimation {
id: rippleAnim
property real x
property real y
property real radius
PropertyAction {
target: ripple
property: "x"
value: rippleAnim.x
}
PropertyAction {
target: ripple
property: "y"
value: rippleAnim.y
}
PropertyAction {
target: ripple
property: "opacity"
value: 0.08
}
Anim {
target: ripple
properties: "implicitWidth,implicitHeight"
from: 0
to: rippleAnim.radius * 2
easing.bezierCurve: MaterialEasing.standardDecel
}
Anim {
target: ripple
property: "opacity"
to: 0
}
}
CustomClippingRect {
id: hoverLayer
anchors.fill: parent
color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0)
radius: root.radius
CustomRect {
id: ripple
radius: 1000
color: root.color
opacity: 0
transform: Translate {
x: -ripple.width / 2
y: -ripple.height / 2
}
}
}
}
+142
View File
@@ -0,0 +1,142 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Services.Pipewire
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import qs.Config
import qs.Components
Item {
id: root
implicitWidth: layout.implicitWidth + 10 * 2
implicitHeight: layout.implicitHeight + 10 * 2
ButtonGroup {
id: sinks
}
ButtonGroup {
id: sources
}
ColumnLayout {
id: layout
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: 12
CustomText {
text: qsTr("Output device")
font.weight: 500
}
Repeater {
model: Audio.sinks
CustomRadioButton {
id: control
required property PwNode modelData
ButtonGroup.group: sinks
checked: Audio.sink?.id === modelData.id
onClicked: Audio.setAudioSink(modelData)
text: modelData.description
}
}
CustomText {
Layout.topMargin: 10
text: qsTr("Input device")
font.weight: 500
}
Repeater {
model: Audio.sources
StyledRadioButton {
required property PwNode modelData
ButtonGroup.group: sources
checked: Audio.source?.id === modelData.id
onClicked: Audio.setAudioSource(modelData)
text: modelData.description
}
}
CustomText {
Layout.topMargin: 10
Layout.bottomMargin: -7 / 2
text: qsTr("Volume (%1)").arg(Audio.muted ? qsTr("Muted") : `${Math.round(Audio.volume * 100)}%`)
font.weight: 500
}
CustomMouseArea {
Layout.fillWidth: true
implicitHeight: 10 * 3
onWheel: event => {
if (event.angleDelta.y > 0)
Audio.incrementVolume();
else if (event.angleDelta.y < 0)
Audio.decrementVolume();
}
CustomSlider {
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: parent.implicitHeight
value: Audio.volume
onMoved: Audio.setVolume(value)
Behavior on value {
Anim {}
}
}
}
CustomRect {
Layout.topMargin: 12
visible: true
implicitWidth: expandBtn.implicitWidth + 10 * 2
implicitHeight: expandBtn.implicitHeight + 5
radius: 17
color: DynamicColors.palette.m3primaryContainer
StateLayer {
color: DynamicColors.palette.m3onPrimaryContainer
function onClicked(): void {
root.wrapper.hasCurrent = false;
Quickshell.execDetached(["app2unit", "--", "pavucontrol"]);
}
}
RowLayout {
id: expandBtn
anchors.centerIn: parent
spacing: Appearance.spacing.small
StyledText {
Layout.leftMargin: Appearance.padding.smaller
text: qsTr("Open settings")
color: Colours.palette.m3onPrimaryContainer
}
MaterialIcon {
text: "chevron_right"
color: Colours.palette.m3onPrimaryContainer
font.pointSize: Appearance.font.size.large
}
}
}
}
}
+6 -3
View File
@@ -10,6 +10,9 @@ Item {
required property int warningThreshold required property int warningThreshold
required property string details required property string details
required property string iconString required property string iconString
property color barColor: Config.useDynamicColors ? DynamicColors.palette.m3primary : Config.accentColor.accents.primary
property color warningBarColor: Config.useDynamicColors ? DynamicColors.palette.m3error : Config.accentColor.accents.warning
property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3onSurface : "#ffffff"
height: columnLayout.childrenRect.height height: columnLayout.childrenRect.height
anchors.left: parent.left anchors.left: parent.left
@@ -29,14 +32,14 @@ Item {
font.family: "Material Symbols Rounded" font.family: "Material Symbols Rounded"
font.pixelSize: 32 font.pixelSize: 32
text: root.iconString text: root.iconString
color: "#ffffff" color: root.textColor
} }
Text { Text {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: root.resourceName text: root.resourceName
font.pixelSize: 14 font.pixelSize: 14
color: "#ffffff" color: root.textColor
} }
} }
@@ -51,7 +54,7 @@ Item {
width: parent.width * Math.min(root.percentage, 1) width: parent.width * Math.min(root.percentage, 1)
height: parent.height height: parent.height
radius: height / 2 radius: height / 2
color: root.percentage * 100 >= root.warningThreshold ? Config.accentColor.accents.warning : Config.accentColor.accents.primary color: root.percentage * 100 >= root.warningThreshold ? root.warningBarColor : root.barColor
Behavior on width { Behavior on width {
Anim { Anim {
+30 -60
View File
@@ -1,3 +1,5 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell
import QtQuick.Layouts import QtQuick.Layouts
@@ -93,49 +95,24 @@ Item {
} }
} }
MouseArea { Item {
id: widgetMouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: {
if (popoutLoader.sourceComponent === null) {
popoutLoader.sourceComponent = resourcePopout;
}
}
}
Loader {
id: popoutLoader
sourceComponent: null
}
component ResourcePopout: PanelWindow {
id: popoutWindow id: popoutWindow
z: 0
property int rectHeight: contentRect.implicitHeight property int rectHeight: contentRect.implicitHeight
WlrLayershell.exclusionMode: ExclusionMode.Ignore anchors.fill: parent
visible: true
anchors { // ShadowRect {
left: true // anchors.fill: contentRect
top: true // radius: 8
right: true // }
bottom: true
}
color: "transparent"
mask: Region { item: contentRect }
ShadowRect {
anchors.fill: contentRect
radius: 8
}
ParallelAnimation { ParallelAnimation {
id: openAnim id: openAnim
Anim { Anim {
target: contentRect target: contentRect
property: "y" property: "implicitHeight"
to: 0 - 1 to: contentColumn.childrenRect.height + 20
duration: MaterialEasing.expressiveEffectsTime duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects easing.bezierCurve: MaterialEasing.expressiveEffects
} }
@@ -145,32 +122,24 @@ Item {
id: closeAnim id: closeAnim
Anim { Anim {
target: contentRect target: contentRect
property: "y" property: "implicitHeight"
to: - contentRect.implicitHeight to: 0
duration: MaterialEasing.expressiveEffectsTime duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects easing.bezierCurve: MaterialEasing.expressiveEffects
} }
onStopped: {
popoutLoader.sourceComponent = null
}
} }
Rectangle { Rectangle {
id: contentRect id: contentRect
x: mapFromItem(root, 0, 0).x anchors.left: parent.left
y: - implicitHeight anchors.right: parent.right
implicitHeight: contentColumn.childrenRect.height + 20 anchors.top: parent.bottom
implicitWidth: root.implicitWidth color: Config.useDynamicColors ? DynamicColors.tPalette.m3surface : Config.baseBgColor
color: Config.baseBgColor border.color: Config.useDynamicColors ? "transparent" : Config.baseBorderColor
border.color: Config.baseBorderColor
border.width: 1 border.width: 1
bottomLeftRadius: 8 bottomLeftRadius: 8
bottomRightRadius: 8 bottomRightRadius: 8
clip: true
Component.onCompleted: {
openAnim.start();
}
Column { Column {
id: contentColumn id: contentColumn
@@ -215,20 +184,21 @@ Item {
.arg( Math.round( ResourceUsage.gpuMemUsage * 100 )) .arg( Math.round( ResourceUsage.gpuMemUsage * 100 ))
} }
} }
}
MouseArea { MouseArea {
id: mouseArea id: widgetMouseArea
anchors.fill: parent z: 1
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: contentRect.bottom
hoverEnabled: true hoverEnabled: true
onEntered: {
openAnim.start();
}
onExited: { onExited: {
closeAnim.start(); closeAnim.start();
} }
} }
} }
} }
Component {
id: resourcePopout
ResourcePopout { }
}
}