better widget style

This commit is contained in:
lucy 2025-12-28 17:36:11 +01:00
parent 484e1a8143
commit e6a8808858
6 changed files with 204 additions and 157 deletions

View File

@ -5,25 +5,25 @@ import Quickshell
Singleton {
id: customColors
// Core Backgrounds
readonly property color background: "#0A0E14"
readonly property color foreground: "#B3B1AD"
readonly property color cursor: "#E6B450"
readonly property color background: "#1A1B26"
readonly property color foreground: "#C0CAF5"
readonly property color cursor: "#C0CAF5"
// The 16 Colors of the Apocalypse
readonly property color color0: "#0A0E14"
readonly property color color1: "#FF3333"
readonly property color color2: "#C2D94C"
readonly property color color3: "#FF8F40"
readonly property color color4: "#59C2FF"
readonly property color color5: "#FFEE99"
readonly property color color6: "#95E6CB"
readonly property color color7: "#B3B1AD"
readonly property color color8: "#4D5566"
readonly property color color9: "#FF3333"
readonly property color color10: "#C2D94C"
readonly property color color11: "#FF8F40"
readonly property color color12: "#59C2FF"
readonly property color color13: "#FFEE99"
readonly property color color14: "#95E6CB"
readonly property color color15: "#B3B1AD"
readonly property color color0: "#414868"
readonly property color color1: "#F7768E"
readonly property color color2: "#9ECE6A"
readonly property color color3: "#E0AF68"
readonly property color color4: "#7AA2F7"
readonly property color color5: "#BB9AF7"
readonly property color color6: "#7DCFFF"
readonly property color color7: "#A9B1D6"
readonly property color color8: "#414868"
readonly property color color9: "#F7768E"
readonly property color color10: "#9ECE6A"
readonly property color color11: "#E0AF68"
readonly property color color12: "#7AA2F7"
readonly property color color13: "#BB9AF7"
readonly property color color14: "#7DCFFF"
readonly property color color15: "#C0CAF5"
}

View File

@ -1,14 +1,12 @@
import Quickshell
import QtQuick
import QtQuick.Layouts
import qs
import "../../"
PanelWindow {
id: root
required property var modelData
implicitHeight: 35
//color: Colors.background
implicitHeight: 34
color: Colors.background
anchors {
top: true

View File

@ -9,81 +9,100 @@ import Quickshell.Widgets
import "../settings/"
import "../../"
RowLayout {
Item {
id: root
implicitWidth: mprisRepeater.implicitWidth + 10
implicitHeight: 34
// 1. Let Repeater loop through the ObjectModel for us
Repeater {
id: mprisRepeater
model: Mpris.players
delegate: RowLayout {
id: delegateLayout
delegate: Item {
required property var modelData
// 2. 🕵 FILTER LOGIC
// Check if this specific player is Spotify.
// We verify 'modelData' exists and check the name.
property bool isSpotify: modelData && modelData.identity.toLowerCase().includes("spotify")
// 3. 👻 HIDE NON-SPOTIFY PLAYERS
visible: isSpotify
// If hidden, take up ZERO space
Layout.preferredWidth: isSpotify ? Math.min(implicitWidth, 400) : 0
Layout.fillHeight: true
// 4. 🎵 USE 'modelData' DIRECTLY
// property string title: modelData.metadata["xesam:title"] || "No Title"
// property string artist: modelData.metadata["xesam:artist"] || "Unknown"
// property string artUrl: modelData.metadata["mpris:artUrl"] || ""
// property bool isPlaying: modelData.playbackStatus === MprisPlaybackStatus.Playing
property string title: modelData.trackTitle
property string artist: modelData.trackArtist
property string artUrl: modelData.trackArtUrl
property bool isPlaying: modelData.isPlaying
spacing: 10
// 🖼 ALBUM ART
ClippingWrapperRectangle {
Layout.alignment: Qt.AlignVCenter
radius: 20
IconImage {
source: delegateLayout.artUrl // Access property from delegate
asynchronous: true
implicitSize: 24
implicitHeight: 34
implicitWidth: delegateLayout.implicitWidth
MouseArea {
id: playbackControl
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: mouse => {
if (mouse.button == Qt.LeftButton) {
console.log("Left button press");
}
if (mouse.button == Qt.RightButton) {
parent.modelData.togglePlaying();
}
}
onDoubleClicked: mouse => {
if (mouse.button == Qt.LeftButton) {
parent.modelData.next();
}
}
}
RowLayout {
id: delegateLayout
// 2. 🕵 FILTER LOGIC
// Check if this specific player is Spotify.
// We verify 'modelData' exists and check the name.
property bool isSpotify: modelData && modelData.identity.toLowerCase().includes("spotify")
// 📝 TEXT INFO
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
spacing: 0
visible: parent.visible
// 3. 👻 HIDE NON-SPOTIFY PLAYERS
visible: isSpotify
Text {
text: delegateLayout.title
color: Colors.foreground
font.bold: true
font.pixelSize: Settings.fontSize
font.family: Settings.font
elide: Text.ElideRight
Layout.preferredWidth: implicitWidth
// If hidden, take up ZERO space
Layout.preferredWidth: isSpotify ? Math.min(implicitWidth, 400) : 0
Layout.fillHeight: true
property string title: modelData.trackTitle
property string artist: modelData.trackArtist
property string artUrl: modelData.trackArtUrl
property bool isPlaying: modelData.isPlaying
spacing: 10
// 🖼 ALBUM ART
ClippingWrapperRectangle {
Layout.alignment: Qt.AlignVCenter
radius: 20
IconImage {
source: delegateLayout.artUrl // Access property from delegate
asynchronous: true
implicitSize: root.implicitHeight * 0.6
}
}
Text {
font.pixelSize: Settings.fontSize - 2
font.family: Settings.font
text: delegateLayout.artist
color: Colors.foreground
opacity: 0.7
Layout.preferredWidth: implicitWidth
// 📝 TEXT INFO
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
spacing: 0
visible: parent.visible
Text {
text: delegateLayout.title
color: Colors.foreground
font.bold: true
font.pixelSize: Settings.fontSize
font.family: Settings.font
elide: Text.ElideRight
Layout.preferredWidth: implicitWidth
}
Text {
font.pixelSize: Settings.fontSize - 2
font.family: Settings.font
text: delegateLayout.artist
color: Colors.foreground
opacity: 0.7
Layout.preferredWidth: implicitWidth
}
}
}
// CONTROLS
}
}
}

View File

@ -1,34 +1,48 @@
import QtQuick
import Quickshell.Services.UPower
import QtQuick.Layouts
import "../settings/"
import "../../"
Item {
id: root
implicitWidth: 80
Text {
id: powerProfile
text: PowerProfile.toString(PowerProfiles.profile)
font.weight: 900
color: Colors.foreground
font.family: Settings.font
font.pixelSize: Settings.fontSize
implicitWidth: powerLayout.implicitWidth + 10
implicitHeight: 34
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: mouse => {
const modes = [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance];
let current = PowerProfiles.profile;
let currentIndex = modes.indexOf(current);
let nextIndex = (currentIndex + 1) % modes.length;
let prevIndex = (currentIndex - 1) % modes.length;
if (mouse.button == Qt.LeftButton)
PowerProfiles.profile = modes[nextIndex];
if (mouse.button == Qt.RightButton)
PowerProfiles.profile = modes[prevIndex];
}
}
ColumnLayout {
id: powerLayout
anchors.centerIn: parent
MouseArea {
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent
onClicked: mouse => {
const modes = [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance];
let current = PowerProfiles.profile;
let currentIndex = modes.indexOf(current);
let nextIndex = (currentIndex + 1) % modes.length;
let prevIndex = (currentIndex - 1) % modes.length;
if (mouse.button == Qt.LeftButton)
PowerProfiles.profile = modes[nextIndex];
if (mouse.button == Qt.RightButton)
PowerProfiles.profile = modes[prevIndex];
}
spacing: 0
Text {
id: powerProfile
text: PowerProfile.toString(PowerProfiles.profile)
font.weight: 900
color: Colors.foreground
font.family: Settings.font
font.pixelSize: Settings.fontSize
}
Text {
text: "Profile"
font.weight: 900
color: Colors.foreground
font.family: Settings.font
font.pixelSize: Settings.fontSize - 2
opacity: 0.7
}
}
}

View File

@ -1,30 +1,29 @@
import QtQuick
import Quickshell.Services.Pipewire
import Quickshell.Widgets
import QtQuick.Layouts
import Quickshell.Io
import "../../"
import "../settings/"
Item {
id: root
implicitWidth: volRow.implicitWidth + 10
implicitHeight: volRow.implicitHeight
// grab the default speaker (Sink)
property var sink: Pipewire.defaultAudioSink
Process {
id: pavu
command: ["pavucontrol"] // The command and args list
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
pavu.startDetached();
}
}
anchors.fill: parent
// Scroll to change volume (The fancy stuff!)
}
implicitWidth: styleLayout.implicitWidth + 10
implicitHeight: 34
property var sink: Pipewire.defaultAudioSink
Process {
id: pavu
command: ["pavucontrol"] // The command and args list
}
// Logic to pick the correct icon name
function getVolumeIcon() {
@ -50,29 +49,37 @@ Item {
return "audio-volume-high";
}
Row {
id: volRow
ColumnLayout {
id: styleLayout
anchors.centerIn: parent
spacing: 5
IconImage {
anchors.verticalCenter: parent.verticalCenter
width: 12
height: 12
source: "root:/icons/" + root.getVolumeIcon() + "-symbolic.svg"
}
Text {
PwObjectTracker {
objects: Pipewire.ready ? Pipewire.defaultAudioSink : []
spacing: 0
Row {
spacing: 10
Text {
PwObjectTracker {
objects: Pipewire.ready ? root.sink : []
}
font.weight: 900
color: Colors.foreground
font.family: Settings.font
font.pixelSize: Settings.fontSize
text: Pipewire.ready ? Math.round(root.sink.audio.volume * 100) + "%" : "0%"
}
width: 20
IconImage {
anchors.verticalCenter: parent.verticalCenter
width: 12
height: 12
source: "root:/icons/" + root.getVolumeIcon() + "-symbolic.svg"
}
}
Text {
font.weight: 900
color: Colors.foreground
font.family: Settings.font
font.pixelSize: Settings.fontSize
text: Pipewire.ready ? Math.round(Pipewire.defaultAudioSink.audio.volume * 100) + "%" : "0%"
font.pixelSize: Settings.fontSize - 2
opacity: 0.7
text: Pipewire.ready ? Pipewire.defaultAudioSink.nickname : "failure"
}
}
}

View File

@ -6,6 +6,8 @@ import Quickshell.Hyprland
import "."
import "../../"
import QtQuick.Layouts
import Quickshell.Widgets
import "../settings/"
WlrLayershell {
id: root
@ -29,7 +31,7 @@ WlrLayershell {
right: true
}
margins {
top: 30
top: 45
right: 10
}
@ -52,7 +54,7 @@ WlrLayershell {
ListView {
id: notifList
anchors.fill: parent
anchors.margins: 10
anchors.margins: 0
// Use 'spacing' to put gaps between notifications
spacing: 10
@ -68,12 +70,12 @@ WlrLayershell {
delegate: Item {
id: notifyItem
implicitWidth: ListView.view.width
implicitHeight: 80 // Fixed height is usually better for icon layouts
implicitHeight: 85 // Fixed height is usually better for icon layouts
required property var modelData
Timer {
id: timout
interval: 3000000
interval: 3000
running: true
onRunningChanged: notifyItem.modelData.dismiss()
}
@ -81,37 +83,40 @@ WlrLayershell {
Rectangle {
anchors.fill: parent
color: Colors.background
radius: 10
radius: 20
border.color: Colors.color5
border.width: 2
// 2. Use RowLayout to put Image | Text side-by-side
RowLayout {
id: fullLayout
anchors.margins: 10
anchors.fill: parent
spacing: 15
spacing: 10
// 🖼 THE IMAGE ON THE LEFT
Image {
// Use the image if available, otherwise hide this space?
// Or you could use an icon fallback.
source: notifyItem.modelData.image
// Hide if no image exists so text takes full width
ClippingWrapperRectangle {
radius: 10
implicitWidth: 64
implicitHeight: 64
visible: notifyItem.modelData.image !== ""
IconImage {
// Fixed size for consistency
sourceSize.width: 48
sourceSize.height: 48
Layout.preferredWidth: 48
Layout.preferredHeight: 48
// Use the image if available, otherwise hide this space?
// Or you could use an icon fallback.
source: notifyItem.modelData.image
// Crop it nicely so it doesn't stretch
fillMode: Image.PreserveAspectCrop
// Hide if no image exists so text takes full width
visible: notifyItem.modelData.image !== ""
// Optional: Cache it for performance
asynchronous: true
// Fixed size for consistency
implicitSize: 30
// Crop it nicely so it doesn't stretch
// Optional: Cache it for performance
asynchronous: true
}
}
// 📝 THE TEXT ON THE RIGHT
@ -119,12 +124,14 @@ WlrLayershell {
id: textLayout
// Take up all remaining width
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignTop // Center vertically
Layout.alignment: Qt.AlignVCenter // Center vertically
spacing: 2
Text {
text: notifyItem.modelData.summary
color: Colors.foreground
font.family: Settings.font
font.pixelSize: Settings.fontSize
font.bold: true
elide: Text.ElideRight
Layout.fillWidth: true
@ -135,7 +142,9 @@ WlrLayershell {
color: Colors.foreground
// Limit to 2 lines
maximumLineCount: 2
font.family: Settings.font
font.pixelSize: Settings.fontSize - 2
maximumLineCount: 3
wrapMode: Text.WordWrap
elide: Text.ElideRight
Layout.fillWidth: true