better widget style
This commit is contained in:
parent
484e1a8143
commit
e6a8808858
38
Colors.qml
38
Colors.qml
@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -9,81 +9,100 @@ 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
|
||||||
// 2. 🕵️♀️ FILTER LOGIC
|
implicitHeight: 34
|
||||||
// Check if this specific player is Spotify.
|
implicitWidth: delegateLayout.implicitWidth
|
||||||
// We verify 'modelData' exists and check the name.
|
MouseArea {
|
||||||
property bool isSpotify: modelData && modelData.identity.toLowerCase().includes("spotify")
|
id: playbackControl
|
||||||
|
anchors.fill: parent
|
||||||
// 3. 👻 HIDE NON-SPOTIFY PLAYERS
|
cursorShape: Qt.PointingHandCursor
|
||||||
visible: isSpotify
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: mouse => {
|
||||||
// If hidden, take up ZERO space
|
if (mouse.button == Qt.LeftButton) {
|
||||||
Layout.preferredWidth: isSpotify ? Math.min(implicitWidth, 400) : 0
|
console.log("Left button press");
|
||||||
Layout.fillHeight: true
|
}
|
||||||
|
if (mouse.button == Qt.RightButton) {
|
||||||
// 4. 🎵 USE 'modelData' DIRECTLY
|
parent.modelData.togglePlaying();
|
||||||
// property string title: modelData.metadata["xesam:title"] || "No Title"
|
}
|
||||||
// property string artist: modelData.metadata["xesam:artist"] || "Unknown"
|
}
|
||||||
// property string artUrl: modelData.metadata["mpris:artUrl"] || ""
|
onDoubleClicked: mouse => {
|
||||||
// property bool isPlaying: modelData.playbackStatus === MprisPlaybackStatus.Playing
|
if (mouse.button == Qt.LeftButton) {
|
||||||
property string title: modelData.trackTitle
|
parent.modelData.next();
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
// 3. 👻 HIDE NON-SPOTIFY PLAYERS
|
||||||
ColumnLayout {
|
visible: isSpotify
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
spacing: 0
|
|
||||||
visible: parent.visible
|
|
||||||
|
|
||||||
Text {
|
// If hidden, take up ZERO space
|
||||||
text: delegateLayout.title
|
Layout.preferredWidth: isSpotify ? Math.min(implicitWidth, 400) : 0
|
||||||
color: Colors.foreground
|
Layout.fillHeight: true
|
||||||
font.bold: true
|
|
||||||
font.pixelSize: Settings.fontSize
|
property string title: modelData.trackTitle
|
||||||
font.family: Settings.font
|
property string artist: modelData.trackArtist
|
||||||
elide: Text.ElideRight
|
property string artUrl: modelData.trackArtUrl
|
||||||
Layout.preferredWidth: implicitWidth
|
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 {
|
// 📝 TEXT INFO
|
||||||
font.pixelSize: Settings.fontSize - 2
|
ColumnLayout {
|
||||||
font.family: Settings.font
|
Layout.alignment: Qt.AlignVCenter
|
||||||
text: delegateLayout.artist
|
spacing: 0
|
||||||
color: Colors.foreground
|
visible: parent.visible
|
||||||
opacity: 0.7
|
|
||||||
Layout.preferredWidth: implicitWidth
|
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,34 +1,48 @@
|
|||||||
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
|
MouseArea {
|
||||||
text: PowerProfile.toString(PowerProfiles.profile)
|
anchors.fill: parent
|
||||||
font.weight: 900
|
cursorShape: Qt.PointingHandCursor
|
||||||
color: Colors.foreground
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
font.family: Settings.font
|
onClicked: mouse => {
|
||||||
font.pixelSize: Settings.fontSize
|
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
|
anchors.centerIn: parent
|
||||||
MouseArea {
|
spacing: 0
|
||||||
cursorShape: Qt.PointingHandCursor
|
Text {
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
id: powerProfile
|
||||||
anchors.fill: parent
|
text: PowerProfile.toString(PowerProfiles.profile)
|
||||||
onClicked: mouse => {
|
font.weight: 900
|
||||||
const modes = [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance];
|
color: Colors.foreground
|
||||||
let current = PowerProfiles.profile;
|
font.family: Settings.font
|
||||||
let currentIndex = modes.indexOf(current);
|
font.pixelSize: Settings.fontSize
|
||||||
let nextIndex = (currentIndex + 1) % modes.length;
|
}
|
||||||
let prevIndex = (currentIndex - 1) % modes.length;
|
Text {
|
||||||
if (mouse.button == Qt.LeftButton)
|
text: "Profile"
|
||||||
PowerProfiles.profile = modes[nextIndex];
|
font.weight: 900
|
||||||
if (mouse.button == Qt.RightButton)
|
color: Colors.foreground
|
||||||
PowerProfiles.profile = modes[prevIndex];
|
font.family: Settings.font
|
||||||
}
|
font.pixelSize: Settings.fontSize - 2
|
||||||
|
opacity: 0.7
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
IconImage {
|
spacing: 10
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
Text {
|
||||||
width: 12
|
PwObjectTracker {
|
||||||
height: 12
|
objects: Pipewire.ready ? root.sink : []
|
||||||
|
}
|
||||||
source: "root:/icons/" + root.getVolumeIcon() + "-symbolic.svg"
|
font.weight: 900
|
||||||
}
|
color: Colors.foreground
|
||||||
|
font.family: Settings.font
|
||||||
Text {
|
font.pixelSize: Settings.fontSize
|
||||||
PwObjectTracker {
|
text: Pipewire.ready ? Math.round(root.sink.audio.volume * 100) + "%" : "0%"
|
||||||
objects: Pipewire.ready ? Pipewire.defaultAudioSink : []
|
|
||||||
}
|
}
|
||||||
width: 20
|
|
||||||
|
IconImage {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 12
|
||||||
|
height: 12
|
||||||
|
source: "root:/icons/" + root.getVolumeIcon() + "-symbolic.svg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,37 +83,40 @@ 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
|
||||||
// Use the image if available, otherwise hide this space?
|
implicitWidth: 64
|
||||||
// Or you could use an icon fallback.
|
implicitHeight: 64
|
||||||
source: notifyItem.modelData.image
|
|
||||||
|
|
||||||
// Hide if no image exists so text takes full width
|
|
||||||
visible: notifyItem.modelData.image !== ""
|
visible: notifyItem.modelData.image !== ""
|
||||||
|
IconImage {
|
||||||
|
|
||||||
// Fixed size for consistency
|
// Use the image if available, otherwise hide this space?
|
||||||
sourceSize.width: 48
|
// Or you could use an icon fallback.
|
||||||
sourceSize.height: 48
|
source: notifyItem.modelData.image
|
||||||
Layout.preferredWidth: 48
|
|
||||||
Layout.preferredHeight: 48
|
|
||||||
|
|
||||||
// Crop it nicely so it doesn't stretch
|
// Hide if no image exists so text takes full width
|
||||||
fillMode: Image.PreserveAspectCrop
|
visible: notifyItem.modelData.image !== ""
|
||||||
|
|
||||||
// Optional: Cache it for performance
|
// Fixed size for consistency
|
||||||
asynchronous: true
|
implicitSize: 30
|
||||||
|
|
||||||
|
// Crop it nicely so it doesn't stretch
|
||||||
|
|
||||||
|
// Optional: Cache it for performance
|
||||||
|
asynchronous: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 📝 THE TEXT ON THE RIGHT
|
// 📝 THE TEXT ON THE RIGHT
|
||||||
@ -119,12 +124,14 @@ WlrLayershell {
|
|||||||
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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user