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

View File

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

View File

@ -9,17 +9,41 @@ import Quickshell.Widgets
import "../settings/" import "../settings/"
import "../../" import "../../"
RowLayout { Item {
id: root id: root
implicitWidth: mprisRepeater.implicitWidth + 10
implicitHeight: 34
// 1. Let Repeater loop through the ObjectModel for us // 1. Let Repeater loop through the ObjectModel for us
Repeater { Repeater {
id: mprisRepeater id: mprisRepeater
model: Mpris.players model: Mpris.players
delegate: RowLayout { delegate: Item {
id: delegateLayout
required property var modelData required property var modelData
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 // 2. 🕵 FILTER LOGIC
// Check if this specific player is Spotify. // Check if this specific player is Spotify.
// We verify 'modelData' exists and check the name. // We verify 'modelData' exists and check the name.
@ -32,11 +56,6 @@ RowLayout {
Layout.preferredWidth: isSpotify ? Math.min(implicitWidth, 400) : 0 Layout.preferredWidth: isSpotify ? Math.min(implicitWidth, 400) : 0
Layout.fillHeight: true 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 title: modelData.trackTitle
property string artist: modelData.trackArtist property string artist: modelData.trackArtist
property string artUrl: modelData.trackArtUrl property string artUrl: modelData.trackArtUrl
@ -45,6 +64,7 @@ RowLayout {
spacing: 10 spacing: 10
// 🖼 ALBUM ART // 🖼 ALBUM ART
ClippingWrapperRectangle { ClippingWrapperRectangle {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
@ -53,7 +73,7 @@ RowLayout {
IconImage { IconImage {
source: delegateLayout.artUrl // Access property from delegate source: delegateLayout.artUrl // Access property from delegate
asynchronous: true asynchronous: true
implicitSize: 24 implicitSize: root.implicitHeight * 0.6
} }
} }
@ -82,8 +102,7 @@ RowLayout {
Layout.preferredWidth: implicitWidth Layout.preferredWidth: implicitWidth
} }
} }
}
// CONTROLS
} }
} }
} }

View File

@ -1,23 +1,17 @@
import QtQuick import QtQuick
import Quickshell.Services.UPower import Quickshell.Services.UPower
import QtQuick.Layouts
import "../settings/" import "../settings/"
import "../../" import "../../"
Item { Item {
id: root id: root
implicitWidth: 80 implicitWidth: powerLayout.implicitWidth + 10
Text { implicitHeight: 34
id: powerProfile
text: PowerProfile.toString(PowerProfiles.profile)
font.weight: 900
color: Colors.foreground
font.family: Settings.font
font.pixelSize: Settings.fontSize
anchors.centerIn: parent
MouseArea { MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent
onClicked: mouse => { onClicked: mouse => {
const modes = [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance]; const modes = [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance];
let current = PowerProfiles.profile; let current = PowerProfiles.profile;
@ -30,5 +24,25 @@ Item {
PowerProfiles.profile = modes[prevIndex]; PowerProfiles.profile = modes[prevIndex];
} }
} }
ColumnLayout {
id: powerLayout
anchors.centerIn: parent
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 QtQuick
import Quickshell.Services.Pipewire import Quickshell.Services.Pipewire
import Quickshell.Widgets import Quickshell.Widgets
import QtQuick.Layouts
import Quickshell.Io import Quickshell.Io
import "../../" import "../../"
import "../settings/" import "../settings/"
Item { Item {
id: root 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 { MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: mouse => { onClicked: mouse => {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
pavu.startDetached(); 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 // Logic to pick the correct icon name
function getVolumeIcon() { function getVolumeIcon() {
@ -50,29 +49,37 @@ Item {
return "audio-volume-high"; return "audio-volume-high";
} }
Row { ColumnLayout {
id: volRow id: styleLayout
anchors.centerIn: parent anchors.centerIn: parent
spacing: 5 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%"
}
IconImage { IconImage {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 12 width: 12
height: 12 height: 12
source: "root:/icons/" + root.getVolumeIcon() + "-symbolic.svg" source: "root:/icons/" + root.getVolumeIcon() + "-symbolic.svg"
} }
Text {
PwObjectTracker {
objects: Pipewire.ready ? Pipewire.defaultAudioSink : []
} }
width: 20 Text {
font.weight: 900 font.weight: 900
color: Colors.foreground color: Colors.foreground
font.family: Settings.font font.family: Settings.font
font.pixelSize: Settings.fontSize font.pixelSize: Settings.fontSize - 2
text: Pipewire.ready ? Math.round(Pipewire.defaultAudioSink.audio.volume * 100) + "%" : "0%" opacity: 0.7
text: Pipewire.ready ? Pipewire.defaultAudioSink.nickname : "failure"
} }
} }
} }

View File

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