Initial commit
5
.stfolder/syncthing-folder-13b209.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# This directory is a Syncthing folder marker.
|
||||||
|
# Do not delete.
|
||||||
|
|
||||||
|
folderID: quickshell
|
||||||
|
created: 2024-12-23T02:09:06-05:00
|
||||||
8
.stversions/PriorityScreens~20250509-203451.qml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
property var screens: ["LG ULTRAGEAR+", "NE135A1M-NY1"]
|
||||||
|
}
|
||||||
126
.stversions/ReloadPopup~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
property bool failed;
|
||||||
|
property string errorString;
|
||||||
|
|
||||||
|
// Connect to the Quickshell global to listen for the reload signals.
|
||||||
|
Connections {
|
||||||
|
target: Quickshell
|
||||||
|
|
||||||
|
function onReloadCompleted() {
|
||||||
|
root.failed = false;
|
||||||
|
popupLoader.loading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onReloadFailed(error: string) {
|
||||||
|
// Close any existing popup before making a new one.
|
||||||
|
popupLoader.active = false;
|
||||||
|
|
||||||
|
root.failed = true;
|
||||||
|
root.errorString = error;
|
||||||
|
popupLoader.loading = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the popup in a loader because it isn't needed most of the timeand will take up
|
||||||
|
// memory that could be used for something else.
|
||||||
|
LazyLoader {
|
||||||
|
id: popupLoader
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: popup
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
}
|
||||||
|
|
||||||
|
margins {
|
||||||
|
top: 25
|
||||||
|
left: 25
|
||||||
|
}
|
||||||
|
|
||||||
|
width: rect.width
|
||||||
|
height: rect.height
|
||||||
|
|
||||||
|
// color blending is a bit odd as detailed in the type reference.
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rect
|
||||||
|
color: failed ? "#40802020" : "#40009020"
|
||||||
|
|
||||||
|
implicitHeight: layout.implicitHeight + 50
|
||||||
|
implicitWidth: layout.implicitWidth + 30
|
||||||
|
|
||||||
|
// Fills the whole area of the rectangle, making any clicks go to it,
|
||||||
|
// which dismiss the popup.
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: popupLoader.active = false
|
||||||
|
|
||||||
|
// makes the mouse area track mouse hovering, so the hide animation
|
||||||
|
// can be paused when hovering.
|
||||||
|
hoverEnabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 20
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.failed ? "Reload failed." : "Reloaded completed!"
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.errorString
|
||||||
|
color: "white"
|
||||||
|
// When visible is false, it also takes up no space.
|
||||||
|
visible: root.errorString != ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A progress bar on the bottom of the screen, showing how long until the
|
||||||
|
// popup is removed.
|
||||||
|
Rectangle {
|
||||||
|
id: bar
|
||||||
|
color: "#20ffffff"
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
height: 20
|
||||||
|
|
||||||
|
PropertyAnimation {
|
||||||
|
id: anim
|
||||||
|
target: bar
|
||||||
|
property: "width"
|
||||||
|
from: rect.width
|
||||||
|
to: 0
|
||||||
|
duration: failed ? 10000 : 800
|
||||||
|
onFinished: popupLoader.active = false
|
||||||
|
|
||||||
|
// Pause the animation when the mouse is hovering over the popup,
|
||||||
|
// so it stays onscreen while reading. This updates reactively
|
||||||
|
// when the mouse moves on and off the popup.
|
||||||
|
paused: mouseArea.containsMouse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could set `running: true` inside the animation, but the width of the
|
||||||
|
// rectangle might not be calculated yet, due to the layout.
|
||||||
|
// In the `Component.onCompleted` event handler, all of the component's
|
||||||
|
// properties and children have been initialized.
|
||||||
|
Component.onCompleted: anim.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
.stversions/ShellGlobals~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
SystemPalette { id: activePalette; colorGroup: SystemPalette.Active }
|
||||||
|
|
||||||
|
readonly property var colors: QtObject {
|
||||||
|
readonly property color accent: activePalette.accent;
|
||||||
|
readonly property color alternateBase: activePalette.alternateBase;
|
||||||
|
readonly property color base: activePalette.base;
|
||||||
|
readonly property color button: activePalette.button;
|
||||||
|
readonly property color buttonText: activePalette.button;
|
||||||
|
readonly property color dark: activePalette.dark;
|
||||||
|
readonly property color highlight: activePalette.highlight;
|
||||||
|
readonly property color textHighlight: activePalette.highlightedText;
|
||||||
|
readonly property color light: activePalette.light;
|
||||||
|
readonly property color mid: activePalette.mid;
|
||||||
|
readonly property color midlight: activePalette.midlight;
|
||||||
|
readonly property color shadow: activePalette.shadow;
|
||||||
|
readonly property color text: activePalette.text;
|
||||||
|
readonly property color window: activePalette.window;
|
||||||
|
readonly property color windowText: activePalette.windowText;
|
||||||
|
readonly property color innerHighlight: "#416563";
|
||||||
|
}
|
||||||
|
}
|
||||||
32
.stversions/ShellGlobals~20250509-203451.qml
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
SystemPalette {
|
||||||
|
id: activePalette
|
||||||
|
colorGroup: SystemPalette.Active
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var colors: QtObject {
|
||||||
|
readonly property color accent: "lightblue"
|
||||||
|
//readonly property color accent: "#5AA097"
|
||||||
|
readonly property color base: "#161616"
|
||||||
|
readonly property color mid: "#1E1F1F"
|
||||||
|
readonly property color light: "#353636"
|
||||||
|
//readonly property color button: activePalette.button
|
||||||
|
//readonly property color buttonText: activePalette.button
|
||||||
|
//readonly property color dark: activePalette.dark
|
||||||
|
readonly property color highlight: activePalette.highlight
|
||||||
|
//readonly property color textHighlight: activePalette.highlightedText
|
||||||
|
//readonly property color light: activePalette.light
|
||||||
|
//readonly property color mid: activePalette.mid
|
||||||
|
readonly property color midlight: activePalette.midlight
|
||||||
|
readonly property color text: activePalette.text
|
||||||
|
//readonly property color window: activePalette.window
|
||||||
|
//readonly property color innerHighlight: "#416563"
|
||||||
|
|
||||||
|
//readonly property color accent: "#5AA097"
|
||||||
|
}
|
||||||
|
}
|
||||||
31
.stversions/bar/ActiveWindow~20250107-005428.qml
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 200;
|
||||||
|
height: parent.height;
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: windowText;
|
||||||
|
text: "";
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 11;
|
||||||
|
visible: text !== "";
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right;
|
||||||
|
verticalCenter: verticalCenter.parent;
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Hyprland;
|
||||||
|
|
||||||
|
function onRawEvent(event) {
|
||||||
|
if (event.name === "activewindow") {
|
||||||
|
windowText.text = event.parse(2)[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
.stversions/bar/ActiveWindow~20250107-005503.qml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 200;
|
||||||
|
height: parent.height;
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: windowText;
|
||||||
|
text: "";
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 11;
|
||||||
|
visible: text !== "";
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right;
|
||||||
|
verticalCenter: verticalCenter.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Hyprland;
|
||||||
|
|
||||||
|
function onRawEvent(event) {
|
||||||
|
if (event.name === "activewindow") {
|
||||||
|
windowText.text = event.parse(2)[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
.stversions/bar/ActiveWindow~20250107-005542.qml
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 200;
|
||||||
|
height: parent.height;
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: windowText;
|
||||||
|
text: "";
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 11;
|
||||||
|
visible: text !== "";
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
//right: parent.right;
|
||||||
|
verticalCenter: verticalCenter.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Hyprland;
|
||||||
|
|
||||||
|
function onRawEvent(event) {
|
||||||
|
if (event.name === "activewindow") {
|
||||||
|
windowText.text = event.parse(2)[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
.stversions/bar/ActiveWindow~20250107-005825.qml
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 200;
|
||||||
|
height: parent.height;
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: windowText;
|
||||||
|
text: "";
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 11;
|
||||||
|
visible: text !== "";
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
|
||||||
|
//anchors {
|
||||||
|
// left: parent.left
|
||||||
|
// //right: parent.right;
|
||||||
|
// verticalCenter: verticalCenter.parent;
|
||||||
|
//}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Hyprland;
|
||||||
|
|
||||||
|
function onRawEvent(event) {
|
||||||
|
if (event.name === "activewindow") {
|
||||||
|
windowText.text = event.parse(2)[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
.stversions/bar/ActiveWindow~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: windowText;
|
||||||
|
text: "";
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 11;
|
||||||
|
visible: text !== "";
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Hyprland;
|
||||||
|
|
||||||
|
function onRawEvent(event) {
|
||||||
|
if (event.name === "activewindow") {
|
||||||
|
windowText.text = event.parse(2)[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
94
.stversions/bar/Bar~20250107-010815.qml
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import "mpris" as Mpris
|
||||||
|
import "notifications" as Notifications
|
||||||
|
import "control" as Control
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root;
|
||||||
|
color: ShellGlobals.colors.window;
|
||||||
|
height: 25
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
Notifications.Notifications {
|
||||||
|
bar: root;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widgets - Everything here is sorted where it appears on the bar.
|
||||||
|
|
||||||
|
// Left
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top;
|
||||||
|
left: parent.left;
|
||||||
|
bottom: parent.bottom;
|
||||||
|
leftMargin: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
Workspaces {}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
visible: activeWindow.visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveWindow {
|
||||||
|
id: activeWindow;
|
||||||
|
Layout.preferredWidth: 250;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Middle
|
||||||
|
|
||||||
|
Mpris.MediaInfo {
|
||||||
|
id: mediaInfo;
|
||||||
|
bar: root;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right
|
||||||
|
RowLayout {
|
||||||
|
spacing: 20;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top;
|
||||||
|
bottom: parent.bottom;
|
||||||
|
right: parent.right;
|
||||||
|
rightMargin: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
SysTray {
|
||||||
|
id: sysTray;
|
||||||
|
bar: root;
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
visible: sysTray.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
BatteryIndicator {
|
||||||
|
id: batteryIndicator
|
||||||
|
}
|
||||||
|
|
||||||
|
Control.Control {
|
||||||
|
bar: root;
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {}
|
||||||
|
|
||||||
|
Clock {
|
||||||
|
id: clock;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
98
.stversions/bar/Bar~20250107-010845.qml
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import "mpris" as Mpris
|
||||||
|
import "notifications" as Notifications
|
||||||
|
import "control" as Control
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root;
|
||||||
|
color: ShellGlobals.colors.window;
|
||||||
|
height: 25
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
Notifications.Notifications {
|
||||||
|
bar: root;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widgets - Everything here is sorted where it appears on the bar.
|
||||||
|
|
||||||
|
// Left
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top;
|
||||||
|
left: parent.left;
|
||||||
|
bottom: parent.bottom;
|
||||||
|
leftMargin: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
Workspaces {}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
visible: activeWindow.visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveWindow {
|
||||||
|
id: activeWindow;
|
||||||
|
Layout.preferredWidth: 250;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Middle
|
||||||
|
|
||||||
|
Mpris.MediaInfo {
|
||||||
|
id: mediaInfo;
|
||||||
|
bar: root;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right
|
||||||
|
RowLayout {
|
||||||
|
spacing: 20;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top;
|
||||||
|
bottom: parent.bottom;
|
||||||
|
right: parent.right;
|
||||||
|
rightMargin: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
SysTray {
|
||||||
|
id: sysTray;
|
||||||
|
bar: root;
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
visible: sysTray.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15;
|
||||||
|
|
||||||
|
BatteryIndicator {
|
||||||
|
id: batteryIndicator
|
||||||
|
}
|
||||||
|
|
||||||
|
Control.Control {
|
||||||
|
bar: root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {}
|
||||||
|
|
||||||
|
Clock {
|
||||||
|
id: clock;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
94
.stversions/bar/Bar~20250107-010938.qml
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import "mpris" as Mpris
|
||||||
|
import "notifications" as Notifications
|
||||||
|
import "control" as Control
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root;
|
||||||
|
color: ShellGlobals.colors.window;
|
||||||
|
height: 25
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
Notifications.Notifications {
|
||||||
|
bar: root;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widgets - Everything here is sorted where it appears on the bar.
|
||||||
|
|
||||||
|
// Left
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top;
|
||||||
|
left: parent.left;
|
||||||
|
bottom: parent.bottom;
|
||||||
|
leftMargin: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
Workspaces {}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
visible: activeWindow.visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveWindow {
|
||||||
|
id: activeWindow;
|
||||||
|
Layout.preferredWidth: 250;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Middle
|
||||||
|
|
||||||
|
Mpris.MediaInfo {
|
||||||
|
id: mediaInfo;
|
||||||
|
bar: root;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top;
|
||||||
|
bottom: parent.bottom;
|
||||||
|
right: parent.right;
|
||||||
|
rightMargin: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
SysTray {
|
||||||
|
id: sysTray;
|
||||||
|
bar: root;
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
visible: sysTray.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
BatteryIndicator {
|
||||||
|
id: batteryIndicator
|
||||||
|
}
|
||||||
|
|
||||||
|
Control.Control {
|
||||||
|
bar: root;
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {}
|
||||||
|
|
||||||
|
Clock {
|
||||||
|
id: clock;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
97
.stversions/bar/Bar~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import "mpris" as Mpris
|
||||||
|
import "notifications" as Notifications
|
||||||
|
import "control" as Control
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root;
|
||||||
|
color: ShellGlobals.colors.window;
|
||||||
|
height: 25
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
Notifications.Notifications {
|
||||||
|
bar: root;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widgets - Everything here is sorted where it appears on the bar.
|
||||||
|
|
||||||
|
// Left
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top;
|
||||||
|
left: parent.left;
|
||||||
|
bottom: parent.bottom;
|
||||||
|
leftMargin: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
Workspaces {}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
visible: activeWindow.visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveWindow {
|
||||||
|
id: activeWindow;
|
||||||
|
Layout.preferredWidth: 250;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Middle
|
||||||
|
Mpris.MediaInfo {
|
||||||
|
id: mediaInfo;
|
||||||
|
bar: root;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top;
|
||||||
|
bottom: parent.bottom;
|
||||||
|
right: parent.right;
|
||||||
|
rightMargin: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
SysTray {
|
||||||
|
id: sysTray;
|
||||||
|
bar: root;
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
visible: sysTray.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 5;
|
||||||
|
|
||||||
|
BatteryIndicator {
|
||||||
|
id: batteryIndicator
|
||||||
|
}
|
||||||
|
|
||||||
|
Control.Control {
|
||||||
|
bar: root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {}
|
||||||
|
|
||||||
|
Clock {
|
||||||
|
id: clock;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
93
.stversions/bar/Bar~20250509-203449.qml
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import "mpris" as Mpris
|
||||||
|
import "volume" as Volume
|
||||||
|
import "../widgets" as Widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
color: ShellGlobals.colors.base
|
||||||
|
height: 25
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Widgets - Everything here is sorted where it appears on the bar.
|
||||||
|
|
||||||
|
// Left
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
bottom: parent.bottom
|
||||||
|
leftMargin: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whatever is available will display
|
||||||
|
HyprWorkspaces {}
|
||||||
|
SwayWorkspaces {}
|
||||||
|
|
||||||
|
Widgets.Separator {
|
||||||
|
visible: activeWindow.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveWindow {
|
||||||
|
id: activeWindow
|
||||||
|
Layout.preferredWidth: 250
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middle
|
||||||
|
Mpris.Status {
|
||||||
|
id: mprisStatus
|
||||||
|
bar: root
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
SysTray {
|
||||||
|
id: sysTray
|
||||||
|
bar: root
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.Separator {
|
||||||
|
visible: sysTray.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
BatteryIndicator {
|
||||||
|
id: batteryIndicator
|
||||||
|
}
|
||||||
|
|
||||||
|
Volume.Button {
|
||||||
|
bar: root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.Separator {}
|
||||||
|
|
||||||
|
Clock {
|
||||||
|
id: clock
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
.stversions/bar/BatteryIndicator~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.UPower
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property string batteryStatus: {
|
||||||
|
if (!UPower.onBattery) {
|
||||||
|
return "charging";
|
||||||
|
}
|
||||||
|
|
||||||
|
let percentage = UPower.displayDevice.percentage * 100;
|
||||||
|
let roundedValue = Math.floor(percentage / 5) * 5;
|
||||||
|
return roundedValue.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
width: 30;
|
||||||
|
height: parent.height;
|
||||||
|
visible: UPower.displayDevice.isLaptopBattery;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellGlobals.colors.highlight;
|
||||||
|
width: 12;
|
||||||
|
height: 8;
|
||||||
|
visible: batteryStatus === "charging";
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
centerIn: batteryImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: batteryImage;
|
||||||
|
implicitSize: 20;
|
||||||
|
source: Qt.resolvedUrl(`../resources/battery/battery-${batteryStatus}.svg`);
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
.stversions/bar/Clock~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Text {
|
||||||
|
property string ap: sysClock.hours >= 12 ? "PM" : "AM";
|
||||||
|
property string minutes: sysClock.minutes.toString().padStart(2, '0');
|
||||||
|
property string hours: {
|
||||||
|
var value = sysClock.hours % 12;
|
||||||
|
if (value === 0) return 12;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemClock {
|
||||||
|
id: sysClock;
|
||||||
|
enabled: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
text: `${hours}:${minutes} ${ap}`
|
||||||
|
font.pointSize: 11;
|
||||||
|
}
|
||||||
51
.stversions/bar/HyprWorkspaces~20250509-203448.qml
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
property var sortedWorkspaces: {
|
||||||
|
let values = Hyprland.workspaces.values.slice();
|
||||||
|
values.sort(function (a, b) {
|
||||||
|
return a.id - b.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: 6
|
||||||
|
visible: Hyprland.monitors.values.length != 0
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: parent.sortedWorkspaces
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
width: 25
|
||||||
|
height: 12
|
||||||
|
radius: 10
|
||||||
|
|
||||||
|
color: {
|
||||||
|
let value = ShellGlobals.colors.light;
|
||||||
|
|
||||||
|
if (!modelData?.id || !Hyprland.focusedMonitor?.activeWorkspace?.id)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
if (workspaceButton.containsMouse) {
|
||||||
|
value = ShellGlobals.colors.midlight;
|
||||||
|
} else if (Hyprland.focusedMonitor.activeWorkspace.id == modelData.id) {
|
||||||
|
value = ShellGlobals.colors.accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: workspaceButton
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onPressed: Hyprland.dispatch(`workspace ${parent.modelData.id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
.stversions/bar/Separator~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import QtQuick
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellGlobals.colors.highlight;
|
||||||
|
radius: 5;
|
||||||
|
width: 7.5;
|
||||||
|
height: 7.5;
|
||||||
|
}
|
||||||
56
.stversions/bar/SwayWorkspaces~20250509-203451.qml
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.I3
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
property var sortedWorkspaces: {
|
||||||
|
let values = I3.workspaces.values.slice();
|
||||||
|
values.sort(function (a, b) {
|
||||||
|
if (!a?.num)
|
||||||
|
return 1;
|
||||||
|
if (!b?.num)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return a.num - b.num;
|
||||||
|
});
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: 6
|
||||||
|
visible: I3.monitors.values.length != 0
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: parent.sortedWorkspaces
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
width: 25
|
||||||
|
height: 12
|
||||||
|
radius: 10
|
||||||
|
|
||||||
|
color: getColor(modelData, workspaceButton.containsMouse)
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: workspaceButton
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onPressed: I3.dispatch(`workspace number ${parent.modelData.num}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColor(modelData, isHovered) {
|
||||||
|
if (!modelData?.id || !I3.focusedMonitor?.focusedWorkspace?.num)
|
||||||
|
return ShellGlobals.colors.light;
|
||||||
|
|
||||||
|
if (isHovered)
|
||||||
|
return ShellGlobals.colors.midlight;
|
||||||
|
|
||||||
|
if (I3.focusedMonitor.focusedWorkspace.num == modelData.num)
|
||||||
|
return ShellGlobals.colors.accent;
|
||||||
|
|
||||||
|
return ShellGlobals.colors.light;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
.stversions/bar/SysTray~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.SystemTray
|
||||||
|
import "../widgets" as Widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
required property var bar;
|
||||||
|
|
||||||
|
spacing: 10;
|
||||||
|
visible: SystemTray.items.values.length > 0
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: SystemTray.items;
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
id: iconButton;
|
||||||
|
implicitSize: 20;
|
||||||
|
source: modelData.icon;
|
||||||
|
|
||||||
|
onClicked: modelData.display(bar, -parent.mapFromGlobal(0, 0).x, root.height+5);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
41
.stversions/bar/SysTray~20250509-203448.qml
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.SystemTray
|
||||||
|
import "../widgets" as Widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: root
|
||||||
|
required property var bar
|
||||||
|
spacing: 10
|
||||||
|
visible: SystemTray.items.values.length > 0
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: SystemTray.items
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
id: iconButton
|
||||||
|
implicitSize: 20
|
||||||
|
source: modelData.icon
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
QsMenuAnchor {
|
||||||
|
id: menuAnchor
|
||||||
|
menu: modelData.menu
|
||||||
|
|
||||||
|
anchor {
|
||||||
|
window: bar
|
||||||
|
adjustment: PopupAdjustment.Flip
|
||||||
|
|
||||||
|
onAnchoring: {
|
||||||
|
anchor.rect = mapToItem(bar.contentItem, -2, height + 4, width + 2, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: menuAnchor.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
.stversions/bar/Workspaces~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import Quickshell.Io
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
property var sortedWorkspaces: {
|
||||||
|
let values = Hyprland.workspaces.values.slice();
|
||||||
|
values.sort(function(a, b) { return a.id - b.id; });
|
||||||
|
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: sortedWorkspaces;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var modelData;
|
||||||
|
width: 25;
|
||||||
|
height: 12;
|
||||||
|
radius: 10;
|
||||||
|
|
||||||
|
color: {
|
||||||
|
let value = ShellGlobals.colors.light;
|
||||||
|
|
||||||
|
if (!modelData?.id || !Hyprland.focusedMonitor?.activeWorkspace?.id)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
if (workspaceButton.containsMouse) {
|
||||||
|
value = ShellGlobals.colors.midlight;
|
||||||
|
} else if (Hyprland.focusedMonitor.activeWorkspace.id == modelData.id) {
|
||||||
|
value = ShellGlobals.colors.highlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: workspaceButton;
|
||||||
|
anchors.fill: parent;
|
||||||
|
hoverEnabled: true;
|
||||||
|
onPressed: Hyprland.dispatch('workspace ' + modelData.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
74
.stversions/bar/control/ControlButton~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Effects
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property string source;
|
||||||
|
property string text: "";
|
||||||
|
property string subText: "";
|
||||||
|
property real implicitSize; // icon implicit size
|
||||||
|
property real padding: 0;
|
||||||
|
property real radius: 5;
|
||||||
|
signal clicked();
|
||||||
|
|
||||||
|
id: root;
|
||||||
|
width: implicitSize*3;
|
||||||
|
height: implicitSize*1.25;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: iconBackground;
|
||||||
|
color: iconButton.containsMouse
|
||||||
|
? ShellGlobals.colors.innerHighlight
|
||||||
|
: ShellGlobals.colors.midlight;
|
||||||
|
border.color: iconButton.containsMouse
|
||||||
|
? ShellGlobals.colors.highlight
|
||||||
|
: ShellGlobals.colors.light;
|
||||||
|
radius: root.radius;
|
||||||
|
anchors.fill: parent;
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 5;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent;
|
||||||
|
margins: root.padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: iconImage;
|
||||||
|
implicitSize: root.implicitSize;
|
||||||
|
source: root.source;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: textLayout;
|
||||||
|
spacing: 3;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.text;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 11;
|
||||||
|
font.bold: true;
|
||||||
|
visible: text.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.subText;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 10;
|
||||||
|
visible: text.length > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: iconButton;
|
||||||
|
hoverEnabled: true;
|
||||||
|
anchors.fill: parent;
|
||||||
|
onPressed: root.clicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
96
.stversions/bar/control/ControlPanel~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Services.UPower
|
||||||
|
import "../../widgets" as Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root;
|
||||||
|
width: controlContainer.implicitWidth+25
|
||||||
|
height: controlContainer.implicitHeight+25
|
||||||
|
//width: 275;
|
||||||
|
//height: 400;
|
||||||
|
color: "transparent"
|
||||||
|
visible: controlContainer.opacity > 0;
|
||||||
|
|
||||||
|
function show(x, y) {
|
||||||
|
root.anchor.rect.x = x;
|
||||||
|
root.anchor.rect.y = y;
|
||||||
|
controlContainer.opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
controlContainer.opacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hoverHandler;
|
||||||
|
enabled: true;
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad;
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered === false) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: controlContainer;
|
||||||
|
color: ShellGlobals.colors.window;
|
||||||
|
radius: 5;
|
||||||
|
opacity: 0; // TODO: change to 0
|
||||||
|
layer.enabled: true;
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true;
|
||||||
|
spread: 0.02;
|
||||||
|
samples: 25;
|
||||||
|
color: "#80000000";
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: columnLayout.implicitWidth + 20 // Add margins
|
||||||
|
implicitHeight: columnLayout.implicitHeight + 20 // Add margins
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
centerIn: parent;
|
||||||
|
margins: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300;
|
||||||
|
easing.type: Easing.OutCubic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: columnLayout
|
||||||
|
spacing: 10;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
margins: 10 // Padding from the parent rectangle
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 10;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 120;
|
||||||
|
height: 120;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 120;
|
||||||
|
height: 120;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
64
.stversions/bar/control/ControlSlider~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
import QtQuick
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: slider;
|
||||||
|
from: 0;
|
||||||
|
to: 100;
|
||||||
|
value: 50;
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
id: sliderContainer;
|
||||||
|
width: slider.availableWidth;
|
||||||
|
height: slider.availableHeight;
|
||||||
|
color: "#e0e0e0";
|
||||||
|
radius: 10;
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: sliderContainer.width;
|
||||||
|
height: sliderContainer.height;
|
||||||
|
radius: sliderContainer.radius;
|
||||||
|
color: "white";
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: sliderContainer.width;
|
||||||
|
height: sliderContainer.height;
|
||||||
|
radius: sliderContainer.radius;
|
||||||
|
color: "black";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: handle;
|
||||||
|
width: sliderContainer.width * (slider.value / slider.to);
|
||||||
|
height: sliderContainer.height;
|
||||||
|
color: ShellGlobals.colors.highlight;
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 100;
|
||||||
|
easing.type: Easing.OutQuad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//IconImage {
|
||||||
|
// implicitSize: 20;
|
||||||
|
// source: "root:resources/control/sleep.svg"
|
||||||
|
//
|
||||||
|
// anchors {
|
||||||
|
// verticalCenter: parent.verticalCenter;
|
||||||
|
// left: parent.left;
|
||||||
|
// leftMargin: 15;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle: Item { }
|
||||||
|
}
|
||||||
70
.stversions/bar/control/ControlVSlider~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
import QtQuick
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: slider;
|
||||||
|
from: 0;
|
||||||
|
to: 100;
|
||||||
|
value: 50;
|
||||||
|
orientation: Qt.Vertical;
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
id: sliderContainer;
|
||||||
|
width: slider.availableWidth;
|
||||||
|
height: slider.availableHeight;
|
||||||
|
color: "#e0e0e0";
|
||||||
|
radius: 10;
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: sliderContainer.width;
|
||||||
|
height: sliderContainer.height;
|
||||||
|
radius: sliderContainer.radius;
|
||||||
|
color: "white";
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: sliderContainer.width;
|
||||||
|
height: sliderContainer.height;
|
||||||
|
radius: sliderContainer.radius;
|
||||||
|
color: "black";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: handle;
|
||||||
|
width: sliderContainer.width;
|
||||||
|
height: sliderContainer.height * (slider.value / slider.to);
|
||||||
|
color: ShellGlobals.colors.highlight;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
bottom: sliderContainer.bottom;
|
||||||
|
horizontalCenter: sliderContainer.horizontalCenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 100;
|
||||||
|
easing.type: Easing.OutQuad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//IconImage {
|
||||||
|
// implicitSize: 20;
|
||||||
|
// source: "root:resources/control/sleep.svg"
|
||||||
|
//
|
||||||
|
// anchors {
|
||||||
|
// verticalCenter: parent.verticalCenter;
|
||||||
|
// left: parent.left;
|
||||||
|
// leftMargin: 15;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle: Item { }
|
||||||
|
}
|
||||||
32
.stversions/bar/control/Control~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../../widgets" as Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
required property var bar;
|
||||||
|
|
||||||
|
id: root;
|
||||||
|
implicitSize: 20;
|
||||||
|
padding: 2;
|
||||||
|
source: "root:/resources/control/controls-button.svg";
|
||||||
|
onClicked: {
|
||||||
|
if (controlLoader.item.visible) {
|
||||||
|
controlLoader.item.hide();
|
||||||
|
} else {
|
||||||
|
controlLoader.item.show(-root.mapFromGlobal(0, 0).x, bar.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: controlLoader;
|
||||||
|
loading: true;
|
||||||
|
|
||||||
|
ControlPanel {
|
||||||
|
id: controlPanel;
|
||||||
|
anchor.window: bar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
238
.stversions/bar/mpris/Card~20250509-203448.qml
Normal file
|
|
@ -0,0 +1,238 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import "../.."
|
||||||
|
import "../../widgets" as Widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var player
|
||||||
|
|
||||||
|
radius: 5
|
||||||
|
color: "transparent"
|
||||||
|
implicitHeight: 220
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: cardLayout
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
leftMargin: 10
|
||||||
|
rightMargin: 10
|
||||||
|
topMargin: 10 // Added top margin for better spacing
|
||||||
|
bottomMargin: 10 // Added bottom margin for better spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mprisImage
|
||||||
|
color: "transparent"
|
||||||
|
radius: 10
|
||||||
|
width: 200
|
||||||
|
height: 200
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
visible: true
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: player.trackArtUrl
|
||||||
|
sourceSize.width: 256
|
||||||
|
sourceSize.height: 256
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: mprisImage.width
|
||||||
|
height: mprisImage.height
|
||||||
|
radius: 10
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: mprisImage.width
|
||||||
|
height: mprisImage.height
|
||||||
|
radius: 10
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true
|
||||||
|
spread: 0.02
|
||||||
|
samples: 25
|
||||||
|
color: "#80000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: player.trackArtist
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 13
|
||||||
|
font.bold: true
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: player.trackTitle
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 13
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: timeStr(player.position)
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
|
||||||
|
font {
|
||||||
|
pointSize: 9
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorQuantizer {
|
||||||
|
id: colorQuantizer
|
||||||
|
source: Qt.resolvedUrl(Media.trackedPlayer?.trackArtUrl ?? "")
|
||||||
|
depth: 0
|
||||||
|
rescaleSize: 64
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: slider
|
||||||
|
from: 0
|
||||||
|
to: player.length
|
||||||
|
enabled: false
|
||||||
|
//enabled: player.canSeek
|
||||||
|
value: player.position
|
||||||
|
|
||||||
|
implicitHeight: 7
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 10
|
||||||
|
Layout.leftMargin: 5
|
||||||
|
Layout.rightMargin: 5
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
id: sliderContainer
|
||||||
|
width: slider.availableWidth
|
||||||
|
height: slider.implicitHeight
|
||||||
|
color: "white"
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: sliderContainer.width
|
||||||
|
height: sliderContainer.height
|
||||||
|
radius: sliderContainer.radius
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: sliderContainer.width
|
||||||
|
height: sliderContainer.height
|
||||||
|
radius: sliderContainer.radius
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: handle
|
||||||
|
width: sliderContainer.width * (slider.value / slider.to)
|
||||||
|
height: sliderContainer.height
|
||||||
|
color: colorQuantizer.colors[0].darker(1.2)
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 100
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle: Rectangle {
|
||||||
|
x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
|
||||||
|
y: slider.topPadding + slider.availableHeight / 2 - height / 2
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
radius: width / 2
|
||||||
|
color: colorQuantizer.colors[0].darker(1.4)
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
horizontalOffset: 0
|
||||||
|
verticalOffset: 1
|
||||||
|
radius: 4.0
|
||||||
|
samples: 9
|
||||||
|
color: "#30000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: timeStr(player.length)
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
|
||||||
|
font {
|
||||||
|
pointSize: 9
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Music Controls
|
||||||
|
RowLayout {
|
||||||
|
spacing: 2
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
implicitSize: 36
|
||||||
|
padding: 4
|
||||||
|
source: "root:resources/mpris/previous.svg"
|
||||||
|
onClicked: player.previous()
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
implicitSize: 36
|
||||||
|
padding: 4
|
||||||
|
source: player?.isPlaying ? "root:resources/mpris/pause.svg" : "root:resources/mpris/play.svg"
|
||||||
|
onClicked: {
|
||||||
|
if (!player.canPlay)
|
||||||
|
return;
|
||||||
|
player.isPlaying ? player.pause() : player.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
implicitSize: 36
|
||||||
|
padding: 4
|
||||||
|
source: "root:resources/mpris/next.svg"
|
||||||
|
onClicked: player.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeStr(time: int): string {
|
||||||
|
const seconds = time % 60;
|
||||||
|
const minutes = Math.floor(time / 60);
|
||||||
|
|
||||||
|
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
95
.stversions/bar/mpris/MediaInfo~20250107-004337.qml
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
required property var bar;
|
||||||
|
|
||||||
|
width: statusInfo.width;
|
||||||
|
height: parent.height;
|
||||||
|
|
||||||
|
MediaSwitcher {
|
||||||
|
id: mediaSwitcher;
|
||||||
|
anchor.window: bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: playButton;
|
||||||
|
hoverEnabled: true;
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: (mouse)=> {
|
||||||
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
mediaSwitcher.visible = !mediaSwitcher.visible;
|
||||||
|
} else {
|
||||||
|
if (!Media.trackedPlayer.canPlay || Media.trackedPlayer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Media.trackedPlayer.isPlaying)
|
||||||
|
Media.trackedPlayer.pause();
|
||||||
|
else
|
||||||
|
Media.trackedPlayer.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: statusInfo;
|
||||||
|
width: statusIcon.width + statusIcon.anchors.rightMargin + nowPlayingText.width;
|
||||||
|
visible: Media.trackedPlayer != null;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter;
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
top: parent.top;
|
||||||
|
bottom: parent.botton;
|
||||||
|
margins: 3.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellGlobals.colors.innerHighlight;
|
||||||
|
border.color: ShellGlobals.colors.highlight;
|
||||||
|
radius: 5;
|
||||||
|
width: parent.width + 25;
|
||||||
|
height: parent.height;
|
||||||
|
visible: playButton.containsMouse;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: statusIcon;
|
||||||
|
implicitSize: 13;
|
||||||
|
source: Media.trackedPlayer?.isPlaying
|
||||||
|
? Qt.resolvedUrl("../../resources/mpris/pause.svg")
|
||||||
|
: Qt.resolvedUrl("../../resources/mpris/play.svg");
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
right: nowPlayingText.left;
|
||||||
|
rightMargin: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: nowPlayingText
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
text: `${Media.trackedPlayer?.trackArtist} - ${Media.trackedPlayer?.trackTitle}`;
|
||||||
|
font.pointSize: 11;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
right: parent.right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncate(text) {
|
||||||
|
if (text?.length > 40) {
|
||||||
|
return text.substring(0, 40) + " ..."
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
96
.stversions/bar/mpris/MediaInfo~20250107-004401.qml
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
required property var bar;
|
||||||
|
|
||||||
|
width: statusInfo.width;
|
||||||
|
height: parent.height;
|
||||||
|
|
||||||
|
MediaSwitcher {
|
||||||
|
id: mediaSwitcher;
|
||||||
|
anchor.window: bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: playButton;
|
||||||
|
hoverEnabled: true;
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: (mouse)=> {
|
||||||
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
mediaSwitcher.visible = !mediaSwitcher.visible;
|
||||||
|
} else {
|
||||||
|
if (!Media.trackedPlayer.canPlay || Media.trackedPlayer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Media.trackedPlayer.isPlaying)
|
||||||
|
Media.trackedPlayer.pause();
|
||||||
|
else
|
||||||
|
Media.trackedPlayer.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: statusInfo;
|
||||||
|
width: statusIcon.width + statusIcon.anchors.rightMargin + nowPlayingText.width;
|
||||||
|
visible: Media.trackedPlayer != null;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter;
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
top: parent.top;
|
||||||
|
bottom: parent.botton;
|
||||||
|
margins: 3.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellGlobals.colors.innerHighlight;
|
||||||
|
border.color: ShellGlobals.colors.highlight;
|
||||||
|
radius: 5;
|
||||||
|
width: parent.width + 25;
|
||||||
|
height: parent.height;
|
||||||
|
visible: playButton.containsMouse;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: statusIcon;
|
||||||
|
implicitSize: 13;
|
||||||
|
source: Media.trackedPlayer?.isPlaying
|
||||||
|
? Qt.resolvedUrl("../../resources/mpris/pause.svg")
|
||||||
|
: Qt.resolvedUrl("../../resources/mpris/play.svg");
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
right: nowPlayingText.left;
|
||||||
|
rightMargin: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: nowPlayingText
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
text: `${Media.trackedPlayer?.trackArtist} - ${Media.trackedPlayer?.trackTitle}`;
|
||||||
|
font.pointSize: 11;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
contentWidth: 100;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
right: parent.right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncate(text) {
|
||||||
|
if (text?.length > 40) {
|
||||||
|
return text.substring(0, 40) + " ..."
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
96
.stversions/bar/mpris/MediaInfo~20250107-004431.qml
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
required property var bar;
|
||||||
|
|
||||||
|
width: statusInfo.width;
|
||||||
|
height: parent.height;
|
||||||
|
|
||||||
|
MediaSwitcher {
|
||||||
|
id: mediaSwitcher;
|
||||||
|
anchor.window: bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: playButton;
|
||||||
|
hoverEnabled: true;
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: (mouse)=> {
|
||||||
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
mediaSwitcher.visible = !mediaSwitcher.visible;
|
||||||
|
} else {
|
||||||
|
if (!Media.trackedPlayer.canPlay || Media.trackedPlayer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Media.trackedPlayer.isPlaying)
|
||||||
|
Media.trackedPlayer.pause();
|
||||||
|
else
|
||||||
|
Media.trackedPlayer.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: statusInfo;
|
||||||
|
width: statusIcon.width + statusIcon.anchors.rightMargin + nowPlayingText.width;
|
||||||
|
visible: Media.trackedPlayer != null;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter;
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
top: parent.top;
|
||||||
|
bottom: parent.botton;
|
||||||
|
margins: 3.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellGlobals.colors.innerHighlight;
|
||||||
|
border.color: ShellGlobals.colors.highlight;
|
||||||
|
radius: 5;
|
||||||
|
width: parent.width + 25;
|
||||||
|
height: parent.height;
|
||||||
|
visible: playButton.containsMouse;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: statusIcon;
|
||||||
|
implicitSize: 13;
|
||||||
|
source: Media.trackedPlayer?.isPlaying
|
||||||
|
? Qt.resolvedUrl("../../resources/mpris/pause.svg")
|
||||||
|
: Qt.resolvedUrl("../../resources/mpris/play.svg");
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
right: nowPlayingText.left;
|
||||||
|
rightMargin: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: nowPlayingText
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
text: `${Media.trackedPlayer?.trackArtist} - ${Media.trackedPlayer?.trackTitle}`;
|
||||||
|
font.pointSize: 11;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
width: 100;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
right: parent.right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncate(text) {
|
||||||
|
if (text?.length > 40) {
|
||||||
|
return text.substring(0, 40) + " ..."
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
96
.stversions/bar/mpris/MediaInfo~20250107-004532.qml
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
required property var bar;
|
||||||
|
|
||||||
|
width: statusInfo.width;
|
||||||
|
height: parent.height;
|
||||||
|
|
||||||
|
MediaSwitcher {
|
||||||
|
id: mediaSwitcher;
|
||||||
|
anchor.window: bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: playButton;
|
||||||
|
hoverEnabled: true;
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: (mouse)=> {
|
||||||
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
mediaSwitcher.visible = !mediaSwitcher.visible;
|
||||||
|
} else {
|
||||||
|
if (!Media.trackedPlayer.canPlay || Media.trackedPlayer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Media.trackedPlayer.isPlaying)
|
||||||
|
Media.trackedPlayer.pause();
|
||||||
|
else
|
||||||
|
Media.trackedPlayer.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: statusInfo;
|
||||||
|
width: statusIcon.width + statusIcon.anchors.rightMargin + nowPlayingText.width;
|
||||||
|
visible: Media.trackedPlayer != null;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter;
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
top: parent.top;
|
||||||
|
bottom: parent.botton;
|
||||||
|
margins: 3.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellGlobals.colors.innerHighlight;
|
||||||
|
border.color: ShellGlobals.colors.highlight;
|
||||||
|
radius: 5;
|
||||||
|
width: parent.width + 25;
|
||||||
|
height: parent.height;
|
||||||
|
visible: playButton.containsMouse;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: statusIcon;
|
||||||
|
implicitSize: 13;
|
||||||
|
source: Media.trackedPlayer?.isPlaying
|
||||||
|
? Qt.resolvedUrl("../../resources/mpris/pause.svg")
|
||||||
|
: Qt.resolvedUrl("../../resources/mpris/play.svg");
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
right: nowPlayingText.left;
|
||||||
|
rightMargin: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: nowPlayingText
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
text: `${Media.trackedPlayer?.trackArtist} - ${Media.trackedPlayer?.trackTitle}`;
|
||||||
|
font.pointSize: 11;
|
||||||
|
width: 250;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
right: parent.right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncate(text) {
|
||||||
|
if (text?.length > 40) {
|
||||||
|
return text.substring(0, 40) + " ..."
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
96
.stversions/bar/mpris/MediaInfo~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
required property var bar;
|
||||||
|
|
||||||
|
width: statusInfo.width;
|
||||||
|
height: parent.height;
|
||||||
|
|
||||||
|
MediaSwitcher {
|
||||||
|
id: mediaSwitcher;
|
||||||
|
anchor.window: bar;
|
||||||
|
anchor.rect.x: parentWindow.width / 2 - width / 2;
|
||||||
|
anchor.rect.y: parentWindow.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: playButton;
|
||||||
|
hoverEnabled: true;
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: (mouse)=> {
|
||||||
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
if (mediaSwitcher.visible) {
|
||||||
|
mediaSwitcher.hide();
|
||||||
|
} else {
|
||||||
|
mediaSwitcher.show();
|
||||||
|
}
|
||||||
|
//mediaSwitcher.visible = !mediaSwitcher.visible;
|
||||||
|
} else {
|
||||||
|
if (!Media.trackedPlayer.canPlay || Media.trackedPlayer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Media.trackedPlayer.isPlaying)
|
||||||
|
Media.trackedPlayer.pause();
|
||||||
|
else
|
||||||
|
Media.trackedPlayer.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: statusInfo;
|
||||||
|
width: statusIcon.width + statusIcon.anchors.rightMargin + nowPlayingText.width;
|
||||||
|
visible: Media.trackedPlayer != null;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter;
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
top: parent.top;
|
||||||
|
bottom: parent.botton;
|
||||||
|
margins: 3.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellGlobals.colors.innerHighlight;
|
||||||
|
border.color: ShellGlobals.colors.highlight;
|
||||||
|
radius: 3;
|
||||||
|
width: parent.width + 25;
|
||||||
|
height: parent.height;
|
||||||
|
visible: playButton.containsMouse;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: statusIcon;
|
||||||
|
implicitSize: 13;
|
||||||
|
source: Media.trackedPlayer?.isPlaying
|
||||||
|
? "root:resources/mpris/pause.svg"
|
||||||
|
: "root:resources/mpris/play.svg";
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
right: nowPlayingText.left;
|
||||||
|
rightMargin: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: nowPlayingText
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
text: `${Media.trackedPlayer?.trackArtist} - ${Media.trackedPlayer?.trackTitle}`;
|
||||||
|
font.pointSize: 11;
|
||||||
|
width: Math.min(implicitWidth, 250);
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
right: parent.right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
122
.stversions/bar/mpris/MediaSwitcher.qml~20250307-175400.bak
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root;
|
||||||
|
width: 500
|
||||||
|
height: 500
|
||||||
|
color: "transparent";
|
||||||
|
visible: false;
|
||||||
|
anchor.rect.x: parentWindow.width / 2 - width / 2
|
||||||
|
anchor.rect.y: parentWindow.height + 5;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellGlobals.colors.window;
|
||||||
|
radius:5;
|
||||||
|
border.color: mouse.hovered
|
||||||
|
? ShellGlobals.colors.highlight
|
||||||
|
: ShellGlobals.colors.light;
|
||||||
|
border.width: 2;
|
||||||
|
anchors.fill: parent;
|
||||||
|
|
||||||
|
// NOTE: You cannot stack mouseArea's that have hovered enabled.
|
||||||
|
// This is the workaround for panel hover detection.
|
||||||
|
HoverHandler {
|
||||||
|
id: mouse
|
||||||
|
enabled: true;
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad;
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered == false) {
|
||||||
|
root.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 5;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter;
|
||||||
|
top: parent.top;
|
||||||
|
margins: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredWidth: 300
|
||||||
|
Layout.preferredHeight: 300
|
||||||
|
source: Media.trackedPlayer.trackArtUrl
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
|
sourceSize {
|
||||||
|
width: 512
|
||||||
|
height: 512
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: truncate(Media.trackedPlayer.trackArtist);
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 13;
|
||||||
|
Layout.alignment: Qt.AlignHCenter;
|
||||||
|
Layout.topMargin: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: truncate(Media.trackedPlayer.trackTitle);
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 13;
|
||||||
|
Layout.alignment: Qt.AlignHCenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 20;
|
||||||
|
Layout.alignment: Qt.AlignHCenter;
|
||||||
|
Layout.topMargin: 10;
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 32
|
||||||
|
source: Qt.resolvedUrl("../../resources/mpris/previous.svg");
|
||||||
|
onClicked: {
|
||||||
|
Media.trackedPlayer.previous();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 36;
|
||||||
|
source: Media.trackedPlayer?.isPlaying
|
||||||
|
? Qt.resolvedUrl("../../resources/mpris/pause.svg")
|
||||||
|
: Qt.resolvedUrl("../../resources/mpris/play.svg");
|
||||||
|
onClicked: {
|
||||||
|
if (!Media.trackedPlayer.canPlay || Media.trackedPlayer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Media.trackedPlayer.isPlaying)
|
||||||
|
Media.trackedPlayer.pause();
|
||||||
|
else
|
||||||
|
Media.trackedPlayer.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 32;
|
||||||
|
source: Qt.resolvedUrl("../../resources/mpris/next.svg");
|
||||||
|
onClicked: {
|
||||||
|
Media.trackedPlayer.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make this some sort of global function
|
||||||
|
function truncate(text) {
|
||||||
|
if (text?.length > 60) {
|
||||||
|
return text.substring(0, 60) + " ..."
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
179
.stversions/bar/mpris/MediaSwitcher~20250107-004917.qml
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root
|
||||||
|
width: mediaPlayerContainer.width + 15;
|
||||||
|
height: mediaPlayerContainer.height + 15;
|
||||||
|
color: "transparent"
|
||||||
|
visible: false
|
||||||
|
anchor.rect.x: parentWindow.width / 2 - width / 2
|
||||||
|
anchor.rect.y: parentWindow.height;
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hoverHandler
|
||||||
|
enabled: true
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered == false) {
|
||||||
|
root.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mediaPlayerContainer;
|
||||||
|
width: 500;
|
||||||
|
height: mediaPlayerColumn.height + 20;
|
||||||
|
color: ShellGlobals.colors.window
|
||||||
|
radius: 5
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true;
|
||||||
|
spread: 0.02;
|
||||||
|
samples: 25;
|
||||||
|
color: "#80000000";
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
|
||||||
|
//border.color: hoverHandler.hovered
|
||||||
|
// ? ShellGlobals.colors.highlight
|
||||||
|
// : ShellGlobals.colors.light
|
||||||
|
//border.width: 2
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mediaPlayerColumn;
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Mpris.players
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
// TODO: do color quant for a background gradient and then blur it
|
||||||
|
required property var modelData;
|
||||||
|
radius: 5;
|
||||||
|
color: ShellGlobals.colors.light;
|
||||||
|
height: 80
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 60
|
||||||
|
Layout.preferredHeight: 60
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mask
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: 5;
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: modelData.trackArtUrl
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: mask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 5
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: modelData.trackArtist;
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 13
|
||||||
|
font.bold: true
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
width: 350;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: modelData.trackTitle;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 13;
|
||||||
|
Layout.alignment: Qt.AlignLeft;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
//width: 350;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spacer to push controls to the right
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controls container
|
||||||
|
RowLayout {
|
||||||
|
spacing: 2
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 24
|
||||||
|
source: Qt.resolvedUrl("../../resources/mpris/previous.svg")
|
||||||
|
onClicked: modelData.previous()
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 24
|
||||||
|
source: modelData?.isPlaying
|
||||||
|
? Qt.resolvedUrl("../../resources/mpris/pause.svg")
|
||||||
|
: Qt.resolvedUrl("../../resources/mpris/play.svg")
|
||||||
|
onClicked: {
|
||||||
|
if (!modelData.canPlay)
|
||||||
|
return
|
||||||
|
modelData.isPlaying
|
||||||
|
? modelData.pause()
|
||||||
|
: modelData.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 24
|
||||||
|
source: Qt.resolvedUrl("../../resources/mpris/next.svg")
|
||||||
|
onClicked: modelData.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncate(text) {
|
||||||
|
if (text?.length > 30) {
|
||||||
|
return text.substring(0, 30) + " ..."
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
178
.stversions/bar/mpris/MediaSwitcher~20250107-004927.qml
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root
|
||||||
|
width: mediaPlayerContainer.width + 15;
|
||||||
|
height: mediaPlayerContainer.height + 15;
|
||||||
|
color: "transparent"
|
||||||
|
visible: false
|
||||||
|
anchor.rect.x: parentWindow.width / 2 - width / 2
|
||||||
|
anchor.rect.y: parentWindow.height;
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hoverHandler
|
||||||
|
enabled: true
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered == false) {
|
||||||
|
root.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mediaPlayerContainer;
|
||||||
|
width: 500;
|
||||||
|
height: mediaPlayerColumn.height + 20;
|
||||||
|
color: ShellGlobals.colors.window
|
||||||
|
radius: 5
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true;
|
||||||
|
spread: 0.02;
|
||||||
|
samples: 25;
|
||||||
|
color: "#80000000";
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
|
||||||
|
//border.color: hoverHandler.hovered
|
||||||
|
// ? ShellGlobals.colors.highlight
|
||||||
|
// : ShellGlobals.colors.light
|
||||||
|
//border.width: 2
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mediaPlayerColumn;
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Mpris.players
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
// TODO: do color quant for a background gradient and then blur it
|
||||||
|
required property var modelData;
|
||||||
|
radius: 5;
|
||||||
|
color: ShellGlobals.colors.light;
|
||||||
|
height: 80
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 60
|
||||||
|
Layout.preferredHeight: 60
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mask
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: 5;
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: modelData.trackArtUrl
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: mask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 5
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: modelData.trackArtist;
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 13
|
||||||
|
font.bold: true
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
width: 350;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: modelData.trackTitle;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 13;
|
||||||
|
Layout.alignment: Qt.AlignLeft;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spacer to push controls to the right
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controls container
|
||||||
|
RowLayout {
|
||||||
|
spacing: 2
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 24
|
||||||
|
source: Qt.resolvedUrl("../../resources/mpris/previous.svg")
|
||||||
|
onClicked: modelData.previous()
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 24
|
||||||
|
source: modelData?.isPlaying
|
||||||
|
? Qt.resolvedUrl("../../resources/mpris/pause.svg")
|
||||||
|
: Qt.resolvedUrl("../../resources/mpris/play.svg")
|
||||||
|
onClicked: {
|
||||||
|
if (!modelData.canPlay)
|
||||||
|
return
|
||||||
|
modelData.isPlaying
|
||||||
|
? modelData.pause()
|
||||||
|
: modelData.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 24
|
||||||
|
source: Qt.resolvedUrl("../../resources/mpris/next.svg")
|
||||||
|
onClicked: modelData.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncate(text) {
|
||||||
|
if (text?.length > 30) {
|
||||||
|
return text.substring(0, 30) + " ..."
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
178
.stversions/bar/mpris/MediaSwitcher~20250107-004947.qml
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root
|
||||||
|
width: mediaPlayerContainer.width + 15;
|
||||||
|
height: mediaPlayerContainer.height + 15;
|
||||||
|
color: "transparent"
|
||||||
|
visible: false
|
||||||
|
anchor.rect.x: parentWindow.width / 2 - width / 2
|
||||||
|
anchor.rect.y: parentWindow.height;
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hoverHandler
|
||||||
|
enabled: true
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered == false) {
|
||||||
|
root.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mediaPlayerContainer;
|
||||||
|
width: 500;
|
||||||
|
height: mediaPlayerColumn.height + 20;
|
||||||
|
color: ShellGlobals.colors.window
|
||||||
|
radius: 5
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true;
|
||||||
|
spread: 0.02;
|
||||||
|
samples: 25;
|
||||||
|
color: "#80000000";
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
|
||||||
|
//border.color: hoverHandler.hovered
|
||||||
|
// ? ShellGlobals.colors.highlight
|
||||||
|
// : ShellGlobals.colors.light
|
||||||
|
//border.width: 2
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mediaPlayerColumn;
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Mpris.players
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
// TODO: do color quant for a background gradient and then blur it
|
||||||
|
required property var modelData;
|
||||||
|
radius: 5;
|
||||||
|
color: ShellGlobals.colors.light;
|
||||||
|
height: 80
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 60
|
||||||
|
Layout.preferredHeight: 60
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mask
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: 5;
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: modelData.trackArtUrl
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: mask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 5
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: modelData.trackArtist;
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 13
|
||||||
|
font.bold: true
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
//width: 350;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: modelData.trackTitle;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 13;
|
||||||
|
Layout.alignment: Qt.AlignLeft;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spacer to push controls to the right
|
||||||
|
//Item {
|
||||||
|
// Layout.fillWidth: true
|
||||||
|
// Layout.minimumWidth: 20
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Controls container
|
||||||
|
RowLayout {
|
||||||
|
spacing: 2
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 24
|
||||||
|
source: Qt.resolvedUrl("../../resources/mpris/previous.svg")
|
||||||
|
onClicked: modelData.previous()
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 24
|
||||||
|
source: modelData?.isPlaying
|
||||||
|
? Qt.resolvedUrl("../../resources/mpris/pause.svg")
|
||||||
|
: Qt.resolvedUrl("../../resources/mpris/play.svg")
|
||||||
|
onClicked: {
|
||||||
|
if (!modelData.canPlay)
|
||||||
|
return
|
||||||
|
modelData.isPlaying
|
||||||
|
? modelData.pause()
|
||||||
|
: modelData.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 24
|
||||||
|
source: Qt.resolvedUrl("../../resources/mpris/next.svg")
|
||||||
|
onClicked: modelData.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncate(text) {
|
||||||
|
if (text?.length > 30) {
|
||||||
|
return text.substring(0, 30) + " ..."
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
171
.stversions/bar/mpris/MediaSwitcher~20250107-005025.qml
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root
|
||||||
|
width: mediaPlayerContainer.width + 15;
|
||||||
|
height: mediaPlayerContainer.height + 15;
|
||||||
|
color: "transparent"
|
||||||
|
visible: false
|
||||||
|
anchor.rect.x: parentWindow.width / 2 - width / 2
|
||||||
|
anchor.rect.y: parentWindow.height;
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hoverHandler
|
||||||
|
enabled: true
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered == false) {
|
||||||
|
root.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mediaPlayerContainer;
|
||||||
|
width: 500;
|
||||||
|
height: mediaPlayerColumn.height + 20;
|
||||||
|
color: ShellGlobals.colors.window
|
||||||
|
radius: 5
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true;
|
||||||
|
spread: 0.02;
|
||||||
|
samples: 25;
|
||||||
|
color: "#80000000";
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
|
||||||
|
//border.color: hoverHandler.hovered
|
||||||
|
// ? ShellGlobals.colors.highlight
|
||||||
|
// : ShellGlobals.colors.light
|
||||||
|
//border.width: 2
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mediaPlayerColumn;
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Mpris.players
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
// TODO: do color quant for a background gradient and then blur it
|
||||||
|
required property var modelData;
|
||||||
|
radius: 5;
|
||||||
|
color: ShellGlobals.colors.light;
|
||||||
|
height: 80
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 60
|
||||||
|
Layout.preferredHeight: 60
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mask
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: 5;
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: modelData.trackArtUrl
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: mask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 5
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: modelData.trackArtist;
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 13
|
||||||
|
font.bold: true
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: modelData.trackTitle;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 13;
|
||||||
|
Layout.alignment: Qt.AlignLeft;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controls container
|
||||||
|
RowLayout {
|
||||||
|
spacing: 2
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 24
|
||||||
|
source: Qt.resolvedUrl("../../resources/mpris/previous.svg")
|
||||||
|
onClicked: modelData.previous()
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 24
|
||||||
|
source: modelData?.isPlaying
|
||||||
|
? Qt.resolvedUrl("../../resources/mpris/pause.svg")
|
||||||
|
: Qt.resolvedUrl("../../resources/mpris/play.svg")
|
||||||
|
onClicked: {
|
||||||
|
if (!modelData.canPlay)
|
||||||
|
return
|
||||||
|
modelData.isPlaying
|
||||||
|
? modelData.pause()
|
||||||
|
: modelData.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
implicitSize: 24
|
||||||
|
source: Qt.resolvedUrl("../../resources/mpris/next.svg")
|
||||||
|
onClicked: modelData.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncate(text) {
|
||||||
|
if (text?.length > 30) {
|
||||||
|
return text.substring(0, 30) + " ..."
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
191
.stversions/bar/mpris/MediaSwitcher~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import "../../widgets/" as Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root
|
||||||
|
width: mediaPlayerContainer.width + 10;
|
||||||
|
height: mediaPlayerContainer.height + 10;
|
||||||
|
color: "transparent"
|
||||||
|
visible: mediaPlayerContainer.opacity > 0;
|
||||||
|
|
||||||
|
anchor.rect.x: parentWindow.width / 2 - width / 2;
|
||||||
|
anchor.rect.y: parentWindow.height;
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
mediaPlayerContainer.opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
mediaPlayerContainer.opacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hoverHandler;
|
||||||
|
enabled: true;
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad;
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered == false) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mediaPlayerContainer;
|
||||||
|
width: 500;
|
||||||
|
height: mediaPlayerColumn.height + 20;
|
||||||
|
color: ShellGlobals.colors.window;
|
||||||
|
radius: 5;
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
layer.enabled: true;
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true;
|
||||||
|
spread: 0.02;
|
||||||
|
samples: 25;
|
||||||
|
color: "#80000000";
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300;
|
||||||
|
easing.type: Easing.OutCubic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mediaPlayerColumn;
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Mpris.players
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var modelData;
|
||||||
|
radius: 5;
|
||||||
|
color: ShellGlobals.colors.midlight;
|
||||||
|
border.color: ShellGlobals.colors.light;
|
||||||
|
height: 75;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15;
|
||||||
|
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent;
|
||||||
|
leftMargin: 10;
|
||||||
|
rightMargin: 10;
|
||||||
|
topMargin: 0;
|
||||||
|
bottomMargin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 60;
|
||||||
|
Layout.preferredHeight: 60;
|
||||||
|
Layout.alignment: Qt.AlignVCenter;
|
||||||
|
visible: modelData.trackArtUrl != "";
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mask;
|
||||||
|
anchors.fill: parent;
|
||||||
|
radius: 5;
|
||||||
|
visible: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent;
|
||||||
|
source: modelData.trackArtUrl;
|
||||||
|
fillMode: Image.PreserveAspectFit;
|
||||||
|
layer.enabled: true;
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
Layout.fillHeight: true;
|
||||||
|
spacing: 5;
|
||||||
|
Layout.alignment: Qt.AlignVCenter;
|
||||||
|
|
||||||
|
Item { Layout.fillHeight: true; }
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: modelData.trackArtist;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 13;
|
||||||
|
font.bold: true;
|
||||||
|
Layout.alignment: Qt.AlignLeft;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: modelData.trackTitle;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 13;
|
||||||
|
Layout.alignment: Qt.AlignLeft;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { Layout.fillHeight: true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 2;
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter;
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
implicitSize: 28;
|
||||||
|
padding: 4;
|
||||||
|
source: "root:resources/mpris/previous.svg";
|
||||||
|
onClicked: modelData.previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
implicitSize: 28;
|
||||||
|
padding: 4;
|
||||||
|
source: modelData?.isPlaying
|
||||||
|
? "root:resources/mpris/pause.svg"
|
||||||
|
: "root:resources/mpris/play.svg";
|
||||||
|
onClicked: {
|
||||||
|
if (!modelData.canPlay)
|
||||||
|
return;
|
||||||
|
modelData.isPlaying
|
||||||
|
? modelData.pause()
|
||||||
|
: modelData.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
implicitSize: 28;
|
||||||
|
padding: 4;
|
||||||
|
source: "root:resources/mpris/next.svg";
|
||||||
|
onClicked: modelData.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
45
.stversions/bar/mpris/Media~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
property MprisPlayer trackedPlayer;
|
||||||
|
|
||||||
|
id: root;
|
||||||
|
|
||||||
|
Instantiator {
|
||||||
|
model: Mpris.players;
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
required property MprisPlayer modelData;
|
||||||
|
target: modelData;
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (root.trackedPlayer == null || modelData.isPlaying) {
|
||||||
|
root.trackedPlayer = modelData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
if (root.trackedPlayer == null || !root.trackedPlayer.isPlaying) {
|
||||||
|
for (const player of Mpris.players.values) {
|
||||||
|
if (player.playbackState.isPlaying) {
|
||||||
|
root.trackedPlayer = player;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedPlayer == null && Mpris.players.values.length != 0) {
|
||||||
|
trackedPlayer = Mpris.players.values[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPlaybackStateChanged() {
|
||||||
|
if (root.trackedPlayer !== modelData) root.trackedPlayer = modelData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
151
.stversions/bar/mpris/Player~20250509-203448.qml
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root
|
||||||
|
width: mediaPlayerContainer.width + 10
|
||||||
|
height: mediaPlayerContainer.height + 10
|
||||||
|
color: "transparent"
|
||||||
|
visible: mediaPlayerContainer.opacity > 0
|
||||||
|
|
||||||
|
anchor.rect.x: parentWindow.width / 2 - width / 2
|
||||||
|
anchor.rect.y: parentWindow.height
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
mediaPlayerContainer.opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
mediaPlayerContainer.opacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hoverHandler
|
||||||
|
enabled: true
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered == false) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mediaPlayerContainer
|
||||||
|
width: 500
|
||||||
|
height: mediaPlayerColumn.height + 20
|
||||||
|
color: ShellGlobals.colors.base
|
||||||
|
radius: 5
|
||||||
|
opacity: 0
|
||||||
|
anchors.centerIn: parent
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: mediaPlayerContainer.width
|
||||||
|
height: mediaPlayerContainer.height
|
||||||
|
radius: mediaPlayerContainer.radius
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: mediaPlayerContainer.width
|
||||||
|
height: mediaPlayerContainer.height
|
||||||
|
radius: mediaPlayerContainer.radius
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true
|
||||||
|
spread: 0.02
|
||||||
|
samples: 25
|
||||||
|
color: "#80000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorQuantizer {
|
||||||
|
id: colorQuantizer
|
||||||
|
source: Qt.resolvedUrl(Media.trackedPlayer?.trackArtUrl ?? "")
|
||||||
|
depth: 2
|
||||||
|
rescaleSize: 64
|
||||||
|
|
||||||
|
onColorsChanged: {
|
||||||
|
Media.colors = colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderEffect {
|
||||||
|
property color topLeftColor: colorQuantizer?.colors[0] ?? "white"
|
||||||
|
property color topRightColor: colorQuantizer?.colors[1] ?? "black"
|
||||||
|
property color bottomLeftColor: colorQuantizer?.colors[2] ?? "white"
|
||||||
|
property color bottomRightColor: colorQuantizer?.colors[3] ?? "black"
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
fragmentShader: "root:/shaders/vertexgradient.frag.qsb"
|
||||||
|
vertexShader: "root:/shaders/vertexgradient.vert.qsb"
|
||||||
|
|
||||||
|
Behavior on topLeftColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on topRightColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on bottomLeftColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on bottomRightColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mediaPlayerColumn
|
||||||
|
spacing: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredWidth: parent.width
|
||||||
|
Layout.margins: 10
|
||||||
|
implicitHeight: childrenRect.height
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// Media Cards
|
||||||
|
Repeater {
|
||||||
|
model: Mpris.players
|
||||||
|
|
||||||
|
Card {
|
||||||
|
required property var modelData
|
||||||
|
player: modelData
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
174
.stversions/bar/mpris/Status~20250509-203451.qml
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
required property var bar
|
||||||
|
|
||||||
|
width: statusInfo.width + 125
|
||||||
|
height: parent.height
|
||||||
|
visible: Mpris.players.values.length != 0
|
||||||
|
|
||||||
|
Player {
|
||||||
|
id: mediaPlayer
|
||||||
|
anchor.window: bar
|
||||||
|
anchor.rect.x: parentWindow.width / 2 - width / 2
|
||||||
|
anchor.rect.y: parentWindow.height
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: playButton
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: mouse => {
|
||||||
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
if (mediaPlayer.visible) {
|
||||||
|
mediaPlayer.hide();
|
||||||
|
} else {
|
||||||
|
mediaPlayer.show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!Media.trackedPlayer.canPlay || Media.trackedPlayer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Media.trackedPlayer.isPlaying)
|
||||||
|
Media.trackedPlayer.pause();
|
||||||
|
else
|
||||||
|
Media.trackedPlayer.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderEffect {
|
||||||
|
id: gradientShader
|
||||||
|
property color topLeftColor: Media?.colors[0] ?? "white"
|
||||||
|
property color topRightColor: Media?.colors[1] ?? "black"
|
||||||
|
property color bottomLeftColor: Media?.colors[2] ?? "white"
|
||||||
|
property color bottomRightColor: Media?.colors[3] ?? "black"
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: false
|
||||||
|
fragmentShader: "root:/shaders/vertexgradient.frag.qsb"
|
||||||
|
vertexShader: "root:/shaders/vertexgradient.vert.qsb"
|
||||||
|
|
||||||
|
Behavior on topLeftColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on topRightColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on bottomLeftColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on bottomRightColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: artRect
|
||||||
|
anchors.fill: gradientShader
|
||||||
|
antialiasing: true
|
||||||
|
visible: false
|
||||||
|
gradient: Gradient {
|
||||||
|
orientation: Gradient.Horizontal
|
||||||
|
GradientStop {
|
||||||
|
position: 0.0
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.5
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 1.0
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OpacityMask {
|
||||||
|
id: clip
|
||||||
|
source: gradientShader
|
||||||
|
anchors.fill: gradientShader
|
||||||
|
maskSource: artRect
|
||||||
|
cached: false
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
GaussianBlur {
|
||||||
|
id: blur
|
||||||
|
visible: root.visible
|
||||||
|
source: clip
|
||||||
|
anchors.fill: clip
|
||||||
|
radius: 16
|
||||||
|
samples: radius * 2
|
||||||
|
transparentBorder: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: statusInfo
|
||||||
|
width: statusIcon.width + statusIcon.anchors.rightMargin + nowPlayingText.width
|
||||||
|
height: parent.height
|
||||||
|
visible: Media.trackedPlayer != null
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.botton
|
||||||
|
margins: 3.5
|
||||||
|
}
|
||||||
|
|
||||||
|
//Rectangle {
|
||||||
|
// color: ShellGlobals.colors.accent
|
||||||
|
// radius: 3
|
||||||
|
// width: parent.width + 25
|
||||||
|
// height: parent.height - 7
|
||||||
|
// visible: playButton.containsMouse
|
||||||
|
// anchors.centerIn: parent
|
||||||
|
//}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: statusIcon
|
||||||
|
implicitSize: 13
|
||||||
|
source: Media.trackedPlayer?.isPlaying ? "root:resources/mpris/pause.svg" : "root:resources/mpris/play.svg"
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
right: nowPlayingText.left
|
||||||
|
rightMargin: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: nowPlayingText
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
text: `${Media.trackedPlayer?.trackArtist} - ${Media.trackedPlayer?.trackTitle}`
|
||||||
|
font.pointSize: 11
|
||||||
|
width: Math.min(implicitWidth, 250)
|
||||||
|
elide: Text.ElideRight
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
required property var bar;
|
||||||
|
|
||||||
|
id: notificationWindow;
|
||||||
|
color: "transparent";
|
||||||
|
width: 550;
|
||||||
|
height: 600;
|
||||||
|
visible: true;
|
||||||
|
mask: Region { item: notifLayout; }
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true;
|
||||||
|
bottom: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
margins {
|
||||||
|
top: 5;
|
||||||
|
bottom: 5;
|
||||||
|
right: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NotificationServer {
|
||||||
|
id: notificationServer;
|
||||||
|
actionsSupported: true;
|
||||||
|
persistenceSupported: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: notificationServer;
|
||||||
|
|
||||||
|
function onNotification(notification) {
|
||||||
|
notification.tracked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: notifLayout;
|
||||||
|
spacing: 5;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left;
|
||||||
|
right: parent.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: notificationServer.trackedNotifications;
|
||||||
|
|
||||||
|
Toast {
|
||||||
|
required property var modelData;
|
||||||
|
notification: modelData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
required property var bar;
|
||||||
|
|
||||||
|
NotificationServer {
|
||||||
|
id: notificationServer;
|
||||||
|
actionsSupported: true;
|
||||||
|
persistenceSupported: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: notificationServer;
|
||||||
|
|
||||||
|
function onNotification(notification) {
|
||||||
|
notificationLoader.item.visible = true;
|
||||||
|
notification.tracked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: notificationLoader;
|
||||||
|
loading: true;
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: notificationWindow;
|
||||||
|
color: "transparent";
|
||||||
|
width: 500;
|
||||||
|
visible: false;
|
||||||
|
exclusionMode: ExclusionMode.Normal;
|
||||||
|
mask: Region { item: notifLayout; }
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true;
|
||||||
|
bottom: true;
|
||||||
|
right: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
margins {
|
||||||
|
top: 5;
|
||||||
|
bottom: 5;
|
||||||
|
right: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: notifLayout;
|
||||||
|
spacing: 15;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top;
|
||||||
|
left: parent.left;
|
||||||
|
right: parent.right;
|
||||||
|
margins: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: notificationServer.trackedNotifications;
|
||||||
|
|
||||||
|
Toast {
|
||||||
|
required property var modelData;
|
||||||
|
notification: modelData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
105
.stversions/bar/notifications/Toast~20241229-025203.qml
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var notification
|
||||||
|
|
||||||
|
radius: 5;
|
||||||
|
color: ShellGlobals.colors.bar;
|
||||||
|
border.color: notificationArea.containsMouse
|
||||||
|
? ShellGlobals.colors.highlight
|
||||||
|
: ShellGlobals.colors.light;
|
||||||
|
border.width: 2;
|
||||||
|
width: parent.width;
|
||||||
|
height: column.implicitHeight + 20;
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: notificationArea;
|
||||||
|
hoverEnabled: true;
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: column;
|
||||||
|
spacing: 5;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent;
|
||||||
|
margins: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 5;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
visible: notification.appIcon == null;
|
||||||
|
source: Qt.resolvedUrl(notification.appIcon);
|
||||||
|
implicitSize: 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: summaryText
|
||||||
|
text: notification.summary
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 14
|
||||||
|
font.bold: true
|
||||||
|
wrapMode: Text.Wrap;
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 16;
|
||||||
|
height: 16;
|
||||||
|
Layout.alignment: Qt.AlighRight | Qt.AlignTop;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "#FF474D";
|
||||||
|
radius: 5;
|
||||||
|
visible: closeButtonArea.containsMouse;
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: closeButtonArea;
|
||||||
|
hoverEnabled: true;
|
||||||
|
anchors.fill: parent;
|
||||||
|
onPressed: {
|
||||||
|
notification.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
source: "image://icon/window-close";
|
||||||
|
implicitSize: 28;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Text {
|
||||||
|
id: bodyText
|
||||||
|
text: notification.body
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 11;
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
//IconImage {
|
||||||
|
// visible: notification.image != null;
|
||||||
|
// source: Qt.resolvedUrl(notification.image);
|
||||||
|
// implicitSize: 25;
|
||||||
|
//}
|
||||||
|
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
105
.stversions/bar/notifications/Toast~20241229-183725.qml
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var notification
|
||||||
|
|
||||||
|
radius: 5;
|
||||||
|
color: ShellGlobals.colors.bar;
|
||||||
|
border.color: notificationArea.containsMouse
|
||||||
|
? ShellGlobals.colors.highlight
|
||||||
|
: ShellGlobals.colors.light;
|
||||||
|
border.width: 2;
|
||||||
|
width: parent.width;
|
||||||
|
height: column.implicitHeight + 20;
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: notificationArea;
|
||||||
|
hoverEnabled: true;
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: column;
|
||||||
|
spacing: 5;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent;
|
||||||
|
margins: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 5;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
|
||||||
|
//IconImage {
|
||||||
|
// visible: notification.appIcon == null;
|
||||||
|
// source: Qt.resolvedUrl(notification.appIcon);
|
||||||
|
// implicitSize: 25;
|
||||||
|
//}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: summaryText
|
||||||
|
text: notification.summary
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 14
|
||||||
|
font.bold: true
|
||||||
|
wrapMode: Text.Wrap;
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 16;
|
||||||
|
height: 16;
|
||||||
|
Layout.alignment: Qt.AlighRight | Qt.AlignTop;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "#FF474D";
|
||||||
|
radius: 5;
|
||||||
|
visible: closeButtonArea.containsMouse;
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: closeButtonArea;
|
||||||
|
hoverEnabled: true;
|
||||||
|
anchors.fill: parent;
|
||||||
|
onPressed: {
|
||||||
|
notification.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
source: "image://icon/window-close";
|
||||||
|
implicitSize: 28;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Text {
|
||||||
|
id: bodyText
|
||||||
|
text: notification.body
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 11;
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
//IconImage {
|
||||||
|
// visible: notification.image != null;
|
||||||
|
// source: Qt.resolvedUrl(notification.image);
|
||||||
|
// implicitSize: 25;
|
||||||
|
//}
|
||||||
|
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
105
.stversions/bar/notifications/Toast~20241229-183824.qml
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var notification
|
||||||
|
|
||||||
|
radius: 5;
|
||||||
|
color: ShellGlobals.colors.bar;
|
||||||
|
border.color: notificationArea.containsMouse
|
||||||
|
? ShellGlobals.colors.highlight
|
||||||
|
: ShellGlobals.colors.light;
|
||||||
|
border.width: 2;
|
||||||
|
width: parent.width;
|
||||||
|
height: column.implicitHeight + 20;
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: notificationArea;
|
||||||
|
hoverEnabled: true;
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: column;
|
||||||
|
spacing: 5;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent;
|
||||||
|
margins: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 5;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
|
||||||
|
//IconImage {
|
||||||
|
// visible: notification.appIcon == null;
|
||||||
|
// source: Qt.resolvedUrl(notification.appIcon);
|
||||||
|
// implicitSize: 25;
|
||||||
|
//}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: summaryText
|
||||||
|
text: notification.summary
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 14
|
||||||
|
font.bold: true
|
||||||
|
wrapMode: Text.Wrap;
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 16;
|
||||||
|
height: 16;
|
||||||
|
Layout.alignment: Qt.AlighRight | Qt.AlignTop;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "#FF474D";
|
||||||
|
radius: 5;
|
||||||
|
visible: closeButtonArea.containsMouse;
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: closeButtonArea;
|
||||||
|
hoverEnabled: true;
|
||||||
|
anchors.fill: parent;
|
||||||
|
onPressed: {
|
||||||
|
notification.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
source: "image://icon/window-close";
|
||||||
|
implicitSize: 28;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Text {
|
||||||
|
id: bodyText
|
||||||
|
text: notification.body
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 11;
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
visible: notification.image != null;
|
||||||
|
source: Qt.resolvedUrl(notification.image);
|
||||||
|
implicitSize: 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
105
.stversions/bar/notifications/Toast~20241229-184059.qml
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var notification
|
||||||
|
|
||||||
|
radius: 5;
|
||||||
|
color: ShellGlobals.colors.bar;
|
||||||
|
border.color: notificationArea.containsMouse
|
||||||
|
? ShellGlobals.colors.highlight
|
||||||
|
: ShellGlobals.colors.light;
|
||||||
|
border.width: 2;
|
||||||
|
width: parent.width;
|
||||||
|
height: column.implicitHeight + 20;
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: notificationArea;
|
||||||
|
hoverEnabled: true;
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: column;
|
||||||
|
spacing: 5;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent;
|
||||||
|
margins: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 5;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
visible: notification.appIcon == null;
|
||||||
|
source: Qt.resolvedUrl(notification.appIcon);
|
||||||
|
implicitSize: 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: summaryText
|
||||||
|
text: notification.summary
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 14
|
||||||
|
font.bold: true
|
||||||
|
wrapMode: Text.Wrap;
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 16;
|
||||||
|
height: 16;
|
||||||
|
Layout.alignment: Qt.AlighRight | Qt.AlignTop;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "#FF474D";
|
||||||
|
radius: 5;
|
||||||
|
visible: closeButtonArea.containsMouse;
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: closeButtonArea;
|
||||||
|
hoverEnabled: true;
|
||||||
|
anchors.fill: parent;
|
||||||
|
onPressed: {
|
||||||
|
notification.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
source: "image://icon/window-close";
|
||||||
|
implicitSize: 28;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Text {
|
||||||
|
id: bodyText
|
||||||
|
text: notification.body
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 11;
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
visible: notification.image != null;
|
||||||
|
source: Qt.resolvedUrl(notification.image);
|
||||||
|
implicitSize: 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
215
.stversions/bar/notifications/Toast~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import ".."
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var notification;
|
||||||
|
|
||||||
|
id: notificationRoot;
|
||||||
|
radius: 5;
|
||||||
|
color: ShellGlobals.colors.window;
|
||||||
|
width: parent.width;
|
||||||
|
height: column.implicitHeight + 30;
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true;
|
||||||
|
spread: 0.01;
|
||||||
|
samples: 25;
|
||||||
|
color: "#80000000";
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property int totalDuration: 5000;
|
||||||
|
property int remainingTime: totalDuration;
|
||||||
|
property bool isRunning: false;
|
||||||
|
property real lastTime: 0;
|
||||||
|
|
||||||
|
id: timerController;
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: internalTimer;
|
||||||
|
interval: 16;
|
||||||
|
repeat: true;
|
||||||
|
running: timerController.isRunning;
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
var currentTime = Date.now()
|
||||||
|
if (timerController.lastTime > 0) {
|
||||||
|
var delta = currentTime - timerController.lastTime;
|
||||||
|
timerController.remainingTime -= delta;
|
||||||
|
if (timerController.remainingTime <= 0) {
|
||||||
|
timerController.isRunning = false;
|
||||||
|
notification.expire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timerController.lastTime = currentTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
if (!isRunning) {
|
||||||
|
lastTime = Date.now();
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pause() {
|
||||||
|
isRunning = false;
|
||||||
|
lastTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: notificationArea;
|
||||||
|
hoverEnabled: true;
|
||||||
|
anchors.fill: parent;
|
||||||
|
|
||||||
|
onContainsMouseChanged: {
|
||||||
|
progressAnimation.paused = containsMouse;
|
||||||
|
if (containsMouse) {
|
||||||
|
timerController.pause();
|
||||||
|
} else {
|
||||||
|
timerController.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: column;
|
||||||
|
spacing: 5;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent;
|
||||||
|
margins: 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: topRow;
|
||||||
|
spacing: 10;
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
visible: notification.appIcon != "";
|
||||||
|
source: Quickshell.iconPath(notification.appIcon);
|
||||||
|
implicitSize: 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Text {
|
||||||
|
id: appName;
|
||||||
|
text: notification.appName;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 11;
|
||||||
|
font.bold: true;
|
||||||
|
wrapMode: Text.Wrap;
|
||||||
|
Layout.fillWidth: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: summaryText;
|
||||||
|
text: notification.summary;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 11;
|
||||||
|
wrapMode: Text.Wrap;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: closeButton;
|
||||||
|
width: 24;
|
||||||
|
height: 24;
|
||||||
|
Layout.alignment: Qt.AlignTop;
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: progressCircle;
|
||||||
|
anchors.fill: parent;
|
||||||
|
antialiasing: true;
|
||||||
|
|
||||||
|
property real progress: 1.0;
|
||||||
|
onProgressChanged: requestPaint();
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
ctx.reset();
|
||||||
|
|
||||||
|
var centerX = width / 2;
|
||||||
|
var centerY = height / 2;
|
||||||
|
var radius = Math.min(width, height) / 2 - 2;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + 2 * Math.PI * progress);
|
||||||
|
ctx.strokeStyle = ShellGlobals.colors.highlight;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: progressAnimation;
|
||||||
|
target: progressCircle;
|
||||||
|
property: "progress";
|
||||||
|
from: 1.0;
|
||||||
|
to: 0.0;
|
||||||
|
duration: 5000;
|
||||||
|
running: true;
|
||||||
|
easing.type: Easing.Linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: closeButtonBg;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
width: 16;
|
||||||
|
height: 16;
|
||||||
|
color: "#FF474D";
|
||||||
|
radius: 10;
|
||||||
|
visible: closeButtonArea.containsMouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: closeButtonArea;
|
||||||
|
hoverEnabled: true;
|
||||||
|
anchors.fill: parent;
|
||||||
|
onPressed: {
|
||||||
|
notification.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
source: "image://icon/window-close";
|
||||||
|
implicitSize: 16;
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
ColumnLayout {
|
||||||
|
Text {
|
||||||
|
id: bodyText;
|
||||||
|
text: notification.body;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
font.pointSize: 11;
|
||||||
|
wrapMode: Text.Wrap;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
35
.stversions/bar/volume/Button~20250509-203451.qml
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import "../../widgets/" as Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
required property var bar;
|
||||||
|
|
||||||
|
id: iconButton
|
||||||
|
implicitSize: 20
|
||||||
|
source: "root:/resources/volume/volume-full.svg"
|
||||||
|
padding: 2
|
||||||
|
|
||||||
|
onClicked:{
|
||||||
|
if (volumeControl.visible) {
|
||||||
|
volumeControl.hide()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
volumeControl.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlPanel {
|
||||||
|
id: volumeControl
|
||||||
|
|
||||||
|
anchor {
|
||||||
|
window: bar
|
||||||
|
|
||||||
|
onAnchoring: {
|
||||||
|
anchor.rect = mapToItem(bar.contentItem, 0, bar.height, width , 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
.stversions/bar/volume/Card~20250509-203451.qml
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
import "../.."
|
||||||
|
import "../../widgets/" as Widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
required property PwNode node
|
||||||
|
color: ShellGlobals.colors.light
|
||||||
|
radius: 5
|
||||||
|
|
||||||
|
PwObjectTracker {
|
||||||
|
id: defaultSourceTracker
|
||||||
|
objects: [root.node]
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
source: {
|
||||||
|
if (!node.properties["application.icon-name"]) {
|
||||||
|
return root.node.audio.muted ? "root:resources/volume/volume-mute.svg" : "root:resources/volume/volume-full.svg";
|
||||||
|
} else {
|
||||||
|
return `image://icon/${node.properties["application.icon-name"]}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitSize: 32
|
||||||
|
padding: 4
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.leftMargin: 5
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.node.audio.muted = !root.node.audio.muted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 4
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
text: {
|
||||||
|
// Taken from quickshell-examples
|
||||||
|
const app = node.properties["application.name"] ?? (node.description != "" ? node.description : node.name);
|
||||||
|
const media = node.properties["media.name"];
|
||||||
|
return media != undefined ? `${app} - ${media}` : app;
|
||||||
|
}
|
||||||
|
|
||||||
|
font.bold: true
|
||||||
|
|
||||||
|
elide: Text.ElideRight
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 5
|
||||||
|
Layout.rightMargin: 5
|
||||||
|
Layout.bottomMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.RoundSlider {
|
||||||
|
implicitHeight: 7
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
value: root.node.audio.volume
|
||||||
|
onValueChanged: node.audio.volume = value
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.rightMargin: 10
|
||||||
|
Layout.bottomMargin: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
117
.stversions/bar/volume/ControlPanel~20250509-203451.qml
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root
|
||||||
|
width: mainContainer.width + 10
|
||||||
|
height: mainContainer.height + 10
|
||||||
|
color: "transparent"
|
||||||
|
visible: mainContainer.opacity > 0
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
mainContainer.opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
mainContainer.opacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hoverHandler
|
||||||
|
enabled: true
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered == false) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mainContainer
|
||||||
|
width: 400
|
||||||
|
height: 400
|
||||||
|
color: ShellGlobals.colors.base
|
||||||
|
radius: 5
|
||||||
|
opacity: 0
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true
|
||||||
|
spread: 0.02
|
||||||
|
samples: 25
|
||||||
|
color: "#80000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mainColumn
|
||||||
|
spacing: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredWidth: parent.width
|
||||||
|
Layout.margins: 10
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
PwNodeLinkTracker {
|
||||||
|
id: linkTracker
|
||||||
|
node: Pipewire.defaultAudioSink
|
||||||
|
}
|
||||||
|
|
||||||
|
Card {
|
||||||
|
node: Pipewire.defaultAudioSink
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 50
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: ShellGlobals.colors.light
|
||||||
|
implicitHeight: 2
|
||||||
|
radius: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
contentWidth: availableWidth
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: linkTracker.linkGroups
|
||||||
|
|
||||||
|
Card {
|
||||||
|
required property PwLinkGroup modelData
|
||||||
|
|
||||||
|
node: modelData.source
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 45
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
285
.stversions/launcher/Controller~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,285 @@
|
||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.SystemTray
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
PersistentProperties {
|
||||||
|
id: persist
|
||||||
|
property bool launcherOpen: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "launcher"
|
||||||
|
|
||||||
|
function open(): void {
|
||||||
|
persist.launcherOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): void {
|
||||||
|
persist.launcherOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(): void {
|
||||||
|
persist.launcherOpen = !persist.launcherOpen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: loader
|
||||||
|
activeAsync: persist.launcherOpen
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
width: 500
|
||||||
|
height: 7 + searchContainer.implicitHeight + list.topMargin * 2 + list.delegateHeight * 10
|
||||||
|
color: "transparent"
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||||
|
WlrLayershell.namespace: "shell:launcher"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Behavior on height { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
|
||||||
|
color: ShellGlobals.colors.window
|
||||||
|
radius: 5
|
||||||
|
|
||||||
|
layer.enabled: true;
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true;
|
||||||
|
spread: 0.02;
|
||||||
|
samples: 25;
|
||||||
|
color: "#80000000";
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 7
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: searchContainer
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: searchbox.implicitHeight + 10
|
||||||
|
radius: 3
|
||||||
|
color: ShellGlobals.colors.midlight;
|
||||||
|
border.color: ShellGlobals.colors.light;
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: searchbox
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 5
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: search;
|
||||||
|
Layout.fillWidth: true;
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
|
||||||
|
focus: true
|
||||||
|
Keys.forwardTo: [list]
|
||||||
|
Keys.onEscapePressed: persist.launcherOpen = false
|
||||||
|
|
||||||
|
Keys.onPressed: event => {
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (event.key == Qt.Key_J) {
|
||||||
|
list.currentIndex = list.currentIndex == list.count - 1 ? 0 : list.currentIndex + 1;
|
||||||
|
event.accepted = true;
|
||||||
|
} else if (event.key == Qt.Key_K) {
|
||||||
|
list.currentIndex = list.currentIndex == 0 ? list.count - 1 : list.currentIndex - 1;
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
if (list.currentItem) {
|
||||||
|
list.currentItem.clicked(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
list.currentIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: list
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
clip: true
|
||||||
|
cacheBuffer: 0 // works around QTBUG-131106
|
||||||
|
//reuseItems: true
|
||||||
|
model: ScriptModel {
|
||||||
|
values: DesktopEntries.applications.values
|
||||||
|
.map(object => {
|
||||||
|
const stxt = search.text.toLowerCase();
|
||||||
|
const ntxt = object.name.toLowerCase();
|
||||||
|
let si = 0;
|
||||||
|
let ni = 0;
|
||||||
|
|
||||||
|
let matches = [];
|
||||||
|
let startMatch = -1;
|
||||||
|
|
||||||
|
for (let si = 0; si != stxt.length; ++si) {
|
||||||
|
const sc = stxt[si];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Drop any entries with letters that don't exist in order
|
||||||
|
if (ni == ntxt.length) return null;
|
||||||
|
|
||||||
|
const nc = ntxt[ni++];
|
||||||
|
|
||||||
|
if (nc == sc) {
|
||||||
|
if (startMatch == -1) startMatch = ni;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (startMatch != -1) {
|
||||||
|
matches.push({
|
||||||
|
index: startMatch,
|
||||||
|
length: ni - startMatch,
|
||||||
|
});
|
||||||
|
|
||||||
|
startMatch = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startMatch != -1) {
|
||||||
|
matches.push({
|
||||||
|
index: startMatch,
|
||||||
|
length: ni - startMatch + 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
object: object,
|
||||||
|
matches: matches,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(entry => entry !== null)
|
||||||
|
.sort((a, b) => {
|
||||||
|
let ai = 0;
|
||||||
|
let bi = 0;
|
||||||
|
let s = 0;
|
||||||
|
|
||||||
|
while (ai != a.matches.length && bi != b.matches.length) {
|
||||||
|
const am = a.matches[ai];
|
||||||
|
const bm = b.matches[bi];
|
||||||
|
|
||||||
|
s = bm.length - am.length;
|
||||||
|
if (s != 0) return s;
|
||||||
|
|
||||||
|
s = am.index - bm.index;
|
||||||
|
if (s != 0) return s;
|
||||||
|
|
||||||
|
++ai;
|
||||||
|
++bi;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = a.matches.length - b.matches.length;
|
||||||
|
if (s != 0) return s;
|
||||||
|
|
||||||
|
s = a.object.name.length - b.object.name.length;
|
||||||
|
if (s != 0) return s;
|
||||||
|
|
||||||
|
return a.object.name.localeCompare(b.object.name);
|
||||||
|
})
|
||||||
|
.map(entry => entry.object);
|
||||||
|
|
||||||
|
onValuesChanged: list.currentIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
topMargin: 7
|
||||||
|
bottomMargin: list.count == 0 ? 0 : 7
|
||||||
|
|
||||||
|
add: Transition {
|
||||||
|
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 100 }
|
||||||
|
}
|
||||||
|
|
||||||
|
displaced: Transition {
|
||||||
|
NumberAnimation { property: "y"; duration: 200; easing.type: Easing.OutCubic }
|
||||||
|
NumberAnimation { property: "opacity"; to: 1; duration: 100 }
|
||||||
|
}
|
||||||
|
|
||||||
|
move: Transition {
|
||||||
|
NumberAnimation { property: "y"; duration: 200; easing.type: Easing.OutCubic }
|
||||||
|
NumberAnimation { property: "opacity"; to: 1; duration: 100 }
|
||||||
|
}
|
||||||
|
|
||||||
|
remove: Transition {
|
||||||
|
NumberAnimation { property: "y"; duration: 200; easing.type: Easing.OutCubic }
|
||||||
|
NumberAnimation { property: "opacity"; to: 0; duration: 100 }
|
||||||
|
}
|
||||||
|
|
||||||
|
highlight: Rectangle {
|
||||||
|
radius: 5
|
||||||
|
color: ShellGlobals.colors.innerHighlight;
|
||||||
|
border.color: ShellGlobals.colors.highlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyNavigationEnabled: true
|
||||||
|
keyNavigationWraps: true
|
||||||
|
highlightMoveVelocity: -1
|
||||||
|
highlightMoveDuration: 100
|
||||||
|
preferredHighlightBegin: list.topMargin
|
||||||
|
preferredHighlightEnd: list.height - list.bottomMargin
|
||||||
|
highlightRangeMode: ListView.ApplyRange
|
||||||
|
snapMode: ListView.SnapToItem
|
||||||
|
|
||||||
|
readonly property real delegateHeight: 44
|
||||||
|
|
||||||
|
delegate: MouseArea {
|
||||||
|
required property DesktopEntry modelData;
|
||||||
|
|
||||||
|
implicitHeight: list.delegateHeight
|
||||||
|
implicitWidth: ListView.view.width
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
modelData.execute();
|
||||||
|
persist.launcherOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: delegateLayout
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
asynchronous: true
|
||||||
|
implicitSize: 30
|
||||||
|
source: Quickshell.iconPath(modelData.icon)
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
text: modelData.name
|
||||||
|
color: ShellGlobals.colors.text;
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {}
|
||||||
|
}
|
||||||
321
.stversions/launcher/Controller~20250509-203448.qml
Normal file
|
|
@ -0,0 +1,321 @@
|
||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.SystemTray
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
PersistentProperties {
|
||||||
|
id: persist
|
||||||
|
property bool launcherOpen: false
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "launcher"
|
||||||
|
|
||||||
|
function open(): void {
|
||||||
|
persist.launcherOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): void {
|
||||||
|
persist.launcherOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(): void {
|
||||||
|
persist.launcherOpen = !persist.launcherOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: loader
|
||||||
|
activeAsync: persist.launcherOpen
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
width: 500
|
||||||
|
height: 7 + searchContainer.implicitHeight + list.topMargin * 2 + list.delegateHeight * 10
|
||||||
|
color: "transparent"
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||||
|
WlrLayershell.namespace: "shell:launcher"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color: ShellGlobals.colors.base
|
||||||
|
radius: 5
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true
|
||||||
|
spread: 0.02
|
||||||
|
samples: 25
|
||||||
|
color: "#80000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 7
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: searchContainer
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: searchbox.implicitHeight + 10
|
||||||
|
radius: 3
|
||||||
|
color: ShellGlobals.colors.midlight
|
||||||
|
border.color: ShellGlobals.colors.light
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: searchbox
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 5
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: search
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
|
||||||
|
focus: true
|
||||||
|
Keys.forwardTo: [list]
|
||||||
|
Keys.onEscapePressed: persist.launcherOpen = false
|
||||||
|
|
||||||
|
Keys.onPressed: event => {
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (event.key == Qt.Key_J) {
|
||||||
|
list.currentIndex = list.currentIndex == list.count - 1 ? 0 : list.currentIndex + 1;
|
||||||
|
event.accepted = true;
|
||||||
|
} else if (event.key == Qt.Key_K) {
|
||||||
|
list.currentIndex = list.currentIndex == 0 ? list.count - 1 : list.currentIndex - 1;
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
if (list.currentItem) {
|
||||||
|
list.currentItem.clicked(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
list.currentIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: list
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
clip: true
|
||||||
|
cacheBuffer: 0 // works around QTBUG-131106
|
||||||
|
//reuseItems: true
|
||||||
|
model: ScriptModel {
|
||||||
|
values: DesktopEntries.applications.values.map(object => {
|
||||||
|
const stxt = search.text.toLowerCase();
|
||||||
|
const ntxt = object.name.toLowerCase();
|
||||||
|
let si = 0;
|
||||||
|
let ni = 0;
|
||||||
|
|
||||||
|
let matches = [];
|
||||||
|
let startMatch = -1;
|
||||||
|
|
||||||
|
for (let si = 0; si != stxt.length; ++si) {
|
||||||
|
const sc = stxt[si];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Drop any entries with letters that don't exist in order
|
||||||
|
if (ni == ntxt.length)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const nc = ntxt[ni++];
|
||||||
|
|
||||||
|
if (nc == sc) {
|
||||||
|
if (startMatch == -1)
|
||||||
|
startMatch = ni;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (startMatch != -1) {
|
||||||
|
matches.push({
|
||||||
|
index: startMatch,
|
||||||
|
length: ni - startMatch
|
||||||
|
});
|
||||||
|
|
||||||
|
startMatch = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startMatch != -1) {
|
||||||
|
matches.push({
|
||||||
|
index: startMatch,
|
||||||
|
length: ni - startMatch + 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
object: object,
|
||||||
|
matches: matches
|
||||||
|
};
|
||||||
|
}).filter(entry => entry !== null).sort((a, b) => {
|
||||||
|
let ai = 0;
|
||||||
|
let bi = 0;
|
||||||
|
let s = 0;
|
||||||
|
|
||||||
|
while (ai != a.matches.length && bi != b.matches.length) {
|
||||||
|
const am = a.matches[ai];
|
||||||
|
const bm = b.matches[bi];
|
||||||
|
|
||||||
|
s = bm.length - am.length;
|
||||||
|
if (s != 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
s = am.index - bm.index;
|
||||||
|
if (s != 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
++ai;
|
||||||
|
++bi;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = a.matches.length - b.matches.length;
|
||||||
|
if (s != 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
s = a.object.name.length - b.object.name.length;
|
||||||
|
if (s != 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
return a.object.name.localeCompare(b.object.name);
|
||||||
|
}).map(entry => entry.object)
|
||||||
|
|
||||||
|
onValuesChanged: list.currentIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
topMargin: 7
|
||||||
|
bottomMargin: list.count == 0 ? 0 : 7
|
||||||
|
|
||||||
|
add: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displaced: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "y"
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
to: 1
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
move: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "y"
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
to: 1
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "y"
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
to: 0
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
highlight: Rectangle {
|
||||||
|
radius: 5
|
||||||
|
color: ShellGlobals.colors.accent
|
||||||
|
}
|
||||||
|
|
||||||
|
keyNavigationEnabled: true
|
||||||
|
keyNavigationWraps: true
|
||||||
|
highlightMoveVelocity: -1
|
||||||
|
highlightMoveDuration: 100
|
||||||
|
preferredHighlightBegin: list.topMargin
|
||||||
|
preferredHighlightEnd: list.height - list.bottomMargin
|
||||||
|
highlightRangeMode: ListView.ApplyRange
|
||||||
|
snapMode: ListView.SnapToItem
|
||||||
|
|
||||||
|
readonly property real delegateHeight: 44
|
||||||
|
|
||||||
|
delegate: MouseArea {
|
||||||
|
required property DesktopEntry modelData
|
||||||
|
|
||||||
|
implicitHeight: list.delegateHeight
|
||||||
|
implicitWidth: ListView.view.width
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
modelData.execute();
|
||||||
|
persist.launcherOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: delegateLayout
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
asynchronous: true
|
||||||
|
implicitSize: 30
|
||||||
|
source: Quickshell.iconPath(modelData.icon)
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
text: modelData.name
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
}
|
||||||
|
}
|
||||||
74
.stversions/notifications/Notifications~20250509-203451.qml
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
required property var screen
|
||||||
|
|
||||||
|
NotificationServer {
|
||||||
|
id: notificationServer
|
||||||
|
actionsSupported: true
|
||||||
|
persistenceSupported: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: notificationServer
|
||||||
|
|
||||||
|
function onNotification(notification) {
|
||||||
|
notificationLoader.item.visible = true;
|
||||||
|
notification.tracked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: notificationLoader
|
||||||
|
loading: true
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: notificationWindow
|
||||||
|
color: "transparent"
|
||||||
|
width: 500
|
||||||
|
visible: false
|
||||||
|
exclusionMode: ExclusionMode.Normal
|
||||||
|
mask: Region {
|
||||||
|
item: notifLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
bottom: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
margins {
|
||||||
|
top: 5
|
||||||
|
bottom: 5
|
||||||
|
right: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: notifLayout
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: notificationServer.trackedNotifications
|
||||||
|
|
||||||
|
Toast {
|
||||||
|
required property var modelData
|
||||||
|
notification: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
.stversions/notifications/Notifications~20250605-105246.qml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
property alias notificationServer: notifServer
|
||||||
|
|
||||||
|
NotificationServer {
|
||||||
|
id: notifServer
|
||||||
|
actionsSupported: true
|
||||||
|
persistenceSupported: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: notifServer
|
||||||
|
|
||||||
|
function onNotification(notification) {
|
||||||
|
notification.tracked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
213
.stversions/notifications/Toast~20250509-203448.qml
Normal file
|
|
@ -0,0 +1,213 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import "../widgets/" as Widgets
|
||||||
|
import ".."
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: notificationRoot
|
||||||
|
required property var notification
|
||||||
|
radius: 5
|
||||||
|
color: ShellGlobals.colors.base
|
||||||
|
width: parent.width
|
||||||
|
height: column.implicitHeight + 30
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true
|
||||||
|
spread: 0.01
|
||||||
|
samples: 25
|
||||||
|
color: "#80000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: timerController
|
||||||
|
property int totalDuration: 5000
|
||||||
|
property int remainingTime: totalDuration
|
||||||
|
property bool isRunning: false
|
||||||
|
property real lastTime: 0
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: internalTimer
|
||||||
|
interval: 16
|
||||||
|
repeat: true
|
||||||
|
running: timerController.isRunning
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
var currentTime = Date.now();
|
||||||
|
if (timerController.lastTime > 0) {
|
||||||
|
var delta = currentTime - timerController.lastTime;
|
||||||
|
timerController.remainingTime -= delta;
|
||||||
|
if (timerController.remainingTime <= 0) {
|
||||||
|
timerController.isRunning = false;
|
||||||
|
notification.expire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timerController.lastTime = currentTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
if (!isRunning) {
|
||||||
|
lastTime = Date.now();
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pause() {
|
||||||
|
isRunning = false;
|
||||||
|
lastTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: notificationArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onContainsMouseChanged: {
|
||||||
|
progressAnimation.paused = containsMouse;
|
||||||
|
if (containsMouse) {
|
||||||
|
timerController.pause();
|
||||||
|
} else {
|
||||||
|
timerController.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: column
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: topRow
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
visible: notification.appIcon != ""
|
||||||
|
source: Quickshell.iconPath(notification.appIcon)
|
||||||
|
implicitSize: 24
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Text {
|
||||||
|
id: appName
|
||||||
|
text: notification.appName
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 11
|
||||||
|
font.bold: true
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
Layout.fillWidth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.Separator {}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: summaryText
|
||||||
|
text: notification.summary
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 11
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: closeButton
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: progressCircle
|
||||||
|
anchors.fill: parent
|
||||||
|
antialiasing: true
|
||||||
|
|
||||||
|
property real progress: 1.0
|
||||||
|
onProgressChanged: requestPaint()
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
ctx.reset();
|
||||||
|
|
||||||
|
var centerX = width / 2;
|
||||||
|
var centerY = height / 2;
|
||||||
|
var radius = Math.min(width, height) / 2 - 2;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + 2 * Math.PI * progress);
|
||||||
|
ctx.strokeStyle = ShellGlobals.colors.accent;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: progressAnimation
|
||||||
|
target: progressCircle
|
||||||
|
property: "progress"
|
||||||
|
from: 1.0
|
||||||
|
to: 0.0
|
||||||
|
duration: 5000
|
||||||
|
running: true
|
||||||
|
easing.type: Easing.Linear
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: closeButtonBg
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
color: "#FF474D"
|
||||||
|
radius: 10
|
||||||
|
visible: closeButtonArea.containsMouse
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: closeButtonArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: {
|
||||||
|
notification.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
source: "image://icon/window-close"
|
||||||
|
implicitSize: 16
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
ColumnLayout {
|
||||||
|
Text {
|
||||||
|
id: bodyText
|
||||||
|
text: notification.body
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
font.pointSize: 11
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M20,15.67V14h2V10H20V8.33A1.34,1.34,0,0,0,18.67,7H3.34A1.34,1.34,0,0,0,2,8.33v7.33A1.34,1.34,0,0,0,3.33,17H18.67A1.34,1.34,0,0,0,20,15.67Z"/>
|
||||||
|
<path d="M20,15.67V14h2V10H20V8.33A1.34,1.34,0,0,0,18.67,7H3.34A1.34,1.34,0,0,0,2,8.33v7.33A1.34,1.34,0,0,0,3.33,17H18.67A1.34,1.34,0,0,0,20,15.67Z" fill="grey"/>
|
||||||
|
<path d="M24,0V24H0V0Z" fill="none"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 580 B |
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M20,15.67V14h2V10H20V8.33A1.34,1.34,0,0,0,18.67,7H3.34A1.34,1.34,0,0,0,2,8.33v7.33A1.34,1.34,0,0,0,3.33,17H18.67A1.34,1.34,0,0,0,20,15.67Z"/>
|
||||||
|
<path d="M20,15.67V14h2V10H20V8.33A1.34,1.34,0,0,0,18.67,7H3.34A1.34,1.34,0,0,0,2,8.33v7.33A1.34,1.34,0,0,0,3.33,17H18.67A1.34,1.34,0,0,0,20,15.67Z" fill="white"/>
|
||||||
|
<path d="M24,0V24H0V0Z" fill="none"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 581 B |
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M24,0V24H0V0Z" fill="none"/>
|
||||||
|
<path d="M20,10V8.33A1.34,1.34,0,0,0,18.67,7H3.34A1.34,1.34,0,0,0,2,8.33v7.33A1.34,1.34,0,0,0,3.33,17H18.67A1.34,1.34,0,0,0,20,15.67V14h2V10Zm-8.5,3v2L4,11H9.5V9L17,13Z" fill="lightgrey"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 452 B |
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M24,0V24H0V0Z" fill="none"/>
|
||||||
|
<path d="M20,10V8.33A1.34,1.34,0,0,0,18.67,7H3.34A1.34,1.34,0,0,0,2,8.33v7.33A1.34,1.34,0,0,0,3.33,17H18.67A1.34,1.34,0,0,0,20,15.67V14h2V10Zm-8.5,3v2L4,11H9.5V9L17,13Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 448 B |
24
.stversions/shell~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
//@ pragma UseQApplication
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import "bar" as Bar
|
||||||
|
import "launcher" as Launcher
|
||||||
|
|
||||||
|
ShellRoot {
|
||||||
|
Component.onCompleted: [Launcher.Controller.init()]
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: Quickshell.screens;
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
property var modelData;
|
||||||
|
|
||||||
|
Bar.Bar {
|
||||||
|
screen: modelData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReloadPopup {}
|
||||||
|
}
|
||||||
51
.stversions/shell~20250509-203449.qml
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
//@ pragma UseQApplication
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import QtQuick
|
||||||
|
import "bar" as Bar
|
||||||
|
import "notifications" as Notifications
|
||||||
|
import "launcher" as Launcher
|
||||||
|
|
||||||
|
ShellRoot {
|
||||||
|
Component.onCompleted: [Launcher.Controller.init()]
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: {
|
||||||
|
// Check PriorityScreens for priortized screens, I only want the bar showing on
|
||||||
|
// screen at a time, because it doesnt make alot of sense to have on multiple
|
||||||
|
// monitors at a time.
|
||||||
|
const screens = Quickshell.screens;
|
||||||
|
console.log("Available Screens: " + screens.map(screen => screen.model));
|
||||||
|
|
||||||
|
const priorityScreen = PriorityScreens.screens.reduce((found, model) => {
|
||||||
|
if (found)
|
||||||
|
return found;
|
||||||
|
return screens.find(screen => screen.model === model);
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
return priorityScreen ? [priorityScreen] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: scope
|
||||||
|
property var modelData
|
||||||
|
|
||||||
|
Bar.Bar {
|
||||||
|
screen: scope.modelData
|
||||||
|
}
|
||||||
|
|
||||||
|
Notifications.Notifications {
|
||||||
|
screen: scope.modelData
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: xPrimaryMoniorSetter
|
||||||
|
running: true
|
||||||
|
command: ["xrandr", "--output", scope.modelData.name, "--primary"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReloadPopup {}
|
||||||
|
}
|
||||||
42
.stversions/widgets/IconButton~20250307-175400.qml
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property string source;
|
||||||
|
property var implicitSize;
|
||||||
|
property var padding: 0;
|
||||||
|
property var radius: 5;
|
||||||
|
signal clicked();
|
||||||
|
|
||||||
|
id: root;
|
||||||
|
implicitWidth: implicitSize;
|
||||||
|
implicitHeight: implicitSize;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: iconBackground;
|
||||||
|
color: ShellGlobals.colors.innerHighlight;
|
||||||
|
border.color: ShellGlobals.colors.highlight;
|
||||||
|
radius: root.radius;
|
||||||
|
visible: iconButton.containsMouse;
|
||||||
|
anchors.fill: parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: iconImage;
|
||||||
|
source: root.source;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent;
|
||||||
|
margins: padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: iconButton;
|
||||||
|
hoverEnabled: true;
|
||||||
|
anchors.fill: parent;
|
||||||
|
onPressed: root.clicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
40
.stversions/widgets/IconButton~20250509-203449.qml
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
property string source
|
||||||
|
property var implicitSize: 24 // Default size if not specified
|
||||||
|
property var padding: 0
|
||||||
|
property var radius: 5
|
||||||
|
signal clicked
|
||||||
|
|
||||||
|
implicitWidth: implicitSize
|
||||||
|
implicitHeight: implicitSize
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: iconBackground
|
||||||
|
color: ShellGlobals.colors.accent
|
||||||
|
radius: root.radius
|
||||||
|
visible: iconButton.containsMouse
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: iconImage
|
||||||
|
source: root.source
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: root.padding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: iconButton
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: root.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
60
.stversions/widgets/RoundSlider~20250509-203448.qml
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: slider
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
id: sliderContainer
|
||||||
|
width: slider.availableWidth
|
||||||
|
height: slider.implicitHeight
|
||||||
|
color: "white"
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: sliderContainer.width
|
||||||
|
height: sliderContainer.height
|
||||||
|
radius: sliderContainer.radius
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: sliderContainer.width
|
||||||
|
height: sliderContainer.height
|
||||||
|
radius: sliderContainer.radius
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: handle
|
||||||
|
width: sliderContainer.width * (slider.value / slider.to)
|
||||||
|
height: sliderContainer.height
|
||||||
|
color: ShellGlobals.colors.accent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle: Rectangle {
|
||||||
|
x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
|
||||||
|
y: slider.topPadding + slider.availableHeight / 2 - height / 2
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
radius: width / 2
|
||||||
|
color: slider.pressed ? ShellGlobals.colors.accent.darker(1.2) : ShellGlobals.colors.accent
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
horizontalOffset: 0
|
||||||
|
verticalOffset: 1
|
||||||
|
radius: 4.0
|
||||||
|
samples: 9
|
||||||
|
color: "#30000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle: Item {}
|
||||||
|
}
|
||||||
9
.stversions/widgets/Separator~20250509-203449.qml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import QtQuick
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellGlobals.colors.accent
|
||||||
|
radius: 5
|
||||||
|
width: 7.5
|
||||||
|
height: 7.5
|
||||||
|
}
|
||||||
0
README.md
Normal file
126
ReloadPopup.qml
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
property bool failed
|
||||||
|
property string errorString
|
||||||
|
|
||||||
|
// Connect to the Quickshell global to listen for the reload signals.
|
||||||
|
Connections {
|
||||||
|
target: Quickshell
|
||||||
|
|
||||||
|
function onReloadCompleted() {
|
||||||
|
root.failed = false;
|
||||||
|
popupLoader.loading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onReloadFailed(error: string) {
|
||||||
|
// Close any existing popup before making a new one.
|
||||||
|
popupLoader.active = false;
|
||||||
|
|
||||||
|
root.failed = true;
|
||||||
|
root.errorString = error;
|
||||||
|
popupLoader.loading = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the popup in a loader because it isn't needed most of the timeand will take up
|
||||||
|
// memory that could be used for something else.
|
||||||
|
LazyLoader {
|
||||||
|
id: popupLoader
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: popup
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
}
|
||||||
|
|
||||||
|
margins {
|
||||||
|
top: 25
|
||||||
|
left: 25
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: rect.width
|
||||||
|
implicitHeight: rect.height
|
||||||
|
|
||||||
|
// color blending is a bit odd as detailed in the type reference.
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rect
|
||||||
|
color: failed ? "#40802020" : "#40009020"
|
||||||
|
|
||||||
|
implicitHeight: layout.implicitHeight + 50
|
||||||
|
implicitWidth: layout.implicitWidth + 30
|
||||||
|
|
||||||
|
// Fills the whole area of the rectangle, making any clicks go to it,
|
||||||
|
// which dismiss the popup.
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: popupLoader.active = false
|
||||||
|
|
||||||
|
// makes the mouse area track mouse hovering, so the hide animation
|
||||||
|
// can be paused when hovering.
|
||||||
|
hoverEnabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 20
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.failed ? "Reload failed." : "Reloaded completed!"
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.errorString
|
||||||
|
color: "white"
|
||||||
|
// When visible is false, it also takes up no space.
|
||||||
|
visible: root.errorString != ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A progress bar on the bottom of the screen, showing how long until the
|
||||||
|
// popup is removed.
|
||||||
|
Rectangle {
|
||||||
|
id: bar
|
||||||
|
color: "#20ffffff"
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
height: 20
|
||||||
|
|
||||||
|
PropertyAnimation {
|
||||||
|
id: anim
|
||||||
|
target: bar
|
||||||
|
property: "width"
|
||||||
|
from: rect.width
|
||||||
|
to: 0
|
||||||
|
duration: failed ? 10000 : 800
|
||||||
|
onFinished: popupLoader.active = false
|
||||||
|
|
||||||
|
// Pause the animation when the mouse is hovering over the popup,
|
||||||
|
// so it stays onscreen while reading. This updates reactively
|
||||||
|
// when the mouse moves on and off the popup.
|
||||||
|
paused: mouseArea.containsMouse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could set `running: true` inside the animation, but the width of the
|
||||||
|
// rectangle might not be calculated yet, due to the layout.
|
||||||
|
// In the `Component.onCompleted` event handler, all of the component's
|
||||||
|
// properties and children have been initialized.
|
||||||
|
Component.onCompleted: anim.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
ShellSettings.qml
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
property alias settings: jsonAdapter
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
// todo change to Quickshell.dataPath("quickshell/settings.json")
|
||||||
|
path: `${Quickshell.env("XDG_DATA_HOME")}/quickshell/settings.json`
|
||||||
|
watchChanges: true
|
||||||
|
// onFileChanged: reload()
|
||||||
|
onAdapterUpdated: writeAdapter()
|
||||||
|
blockLoading: true
|
||||||
|
|
||||||
|
JsonAdapter {
|
||||||
|
id: jsonAdapter
|
||||||
|
property int barHeight: 25
|
||||||
|
property string wallpaperUrl: Qt.resolvedUrl("root:resources/wallpapers/pixelart0.jpg")
|
||||||
|
property string colorScheme: "scheme-fruit-salad"
|
||||||
|
|
||||||
|
property var colors: {
|
||||||
|
"background": "#131313",
|
||||||
|
"error": "#ffb4ab",
|
||||||
|
"error_container": "#93000a",
|
||||||
|
"inverse_on_surface": "#303030",
|
||||||
|
"inverse_primary": "#9c4236",
|
||||||
|
"inverse_surface": "#e2e2e2",
|
||||||
|
"on_background": "#e2e2e2",
|
||||||
|
"on_error": "#690005",
|
||||||
|
"on_error_container": "#ffdad6",
|
||||||
|
"on_primary": "#5f150d",
|
||||||
|
"on_primary_container": "#ffdad4",
|
||||||
|
"on_primary_fixed": "#410000",
|
||||||
|
"on_primary_fixed_variant": "#7d2b21",
|
||||||
|
"on_secondary": "#442925",
|
||||||
|
"on_secondary_container": "#ffdad4",
|
||||||
|
"on_secondary_fixed": "#2c1512",
|
||||||
|
"on_secondary_fixed_variant": "#5d3f3b",
|
||||||
|
"on_surface": "#e2e2e2",
|
||||||
|
"on_surface_variant": "#c6c6c6",
|
||||||
|
"on_tertiary": "#3e2e04",
|
||||||
|
"on_tertiary_container": "#fbdfa6",
|
||||||
|
"on_tertiary_fixed": "#251a00",
|
||||||
|
"on_tertiary_fixed_variant": "#564419",
|
||||||
|
"outline": "#919191",
|
||||||
|
"outline_variant": "#474747",
|
||||||
|
"primary": "#ffb4a8",
|
||||||
|
"primary_container": "#7d2b21",
|
||||||
|
"primary_fixed": "#ffdad4",
|
||||||
|
"primary_fixed_dim": "#ffb4a8",
|
||||||
|
"scrim": "#000000",
|
||||||
|
"secondary": "#e7bdb6",
|
||||||
|
"secondary_container": "#5d3f3b",
|
||||||
|
"secondary_fixed": "#ffdad4",
|
||||||
|
"secondary_fixed_dim": "#e7bdb6",
|
||||||
|
"shadow": "#000000",
|
||||||
|
"source_color": "#df4332",
|
||||||
|
"surface": "#131313",
|
||||||
|
"surface_bright": "#393939",
|
||||||
|
"surface_container": "#1f1f1f",
|
||||||
|
"surface_container_high": "#2a2a2a",
|
||||||
|
"surface_container_highest": "#353535",
|
||||||
|
"surface_container_low": "#1b1b1b",
|
||||||
|
"surface_container_lowest": "#0e0e0e",
|
||||||
|
"surface_dim": "#131313",
|
||||||
|
"surface_tint": "#ffb4a8",
|
||||||
|
"surface_variant": "#474747",
|
||||||
|
"tertiary": "#dec38c",
|
||||||
|
"tertiary_container": "#564419",
|
||||||
|
"tertiary_fixed": "#fbdfa6",
|
||||||
|
"tertiary_fixed_dim": "#dec38c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
bar/ActiveWindow.qml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: windowText
|
||||||
|
text: ToplevelManager.activeToplevel?.title ?? ""
|
||||||
|
color: ShellSettings.settings.colors["inverse_surface"]
|
||||||
|
font.pointSize: 11
|
||||||
|
visible: text !== ""
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
94
bar/Bar.qml
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import "control" as Control
|
||||||
|
import "systray" as SysTray
|
||||||
|
import "notifications" as Notifications
|
||||||
|
import "popups" as Popup
|
||||||
|
import "../widgets" as Widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
color: ShellSettings.settings.colors["surface"]
|
||||||
|
implicitHeight: ShellSettings.settings.barHeight
|
||||||
|
property alias popup: popupWindow
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Popup window for all popups
|
||||||
|
Popup.MenuWindow {
|
||||||
|
id: popupWindow
|
||||||
|
bar: root
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
bottom: parent.bottom
|
||||||
|
leftMargin: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
HyprWorkspaces {
|
||||||
|
Layout.fillWidth: false
|
||||||
|
Layout.preferredHeight: parent.height
|
||||||
|
Layout.margins: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.Separator {
|
||||||
|
visible: activeWindow.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveWindow {
|
||||||
|
id: activeWindow
|
||||||
|
Layout.preferredWidth: 400
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
SysTray.SysTray {
|
||||||
|
id: sysTray
|
||||||
|
popup: root.popup
|
||||||
|
Layout.rightMargin: 300
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notifications.NotificationButton {
|
||||||
|
// implicitSize: 16
|
||||||
|
// bar: root
|
||||||
|
// }
|
||||||
|
|
||||||
|
BatteryIndicator {
|
||||||
|
id: batteryIndicator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control.Button {
|
||||||
|
// bar: root
|
||||||
|
// screen: root
|
||||||
|
// }
|
||||||
|
|
||||||
|
Widgets.Separator {}
|
||||||
|
|
||||||
|
Clock {
|
||||||
|
id: clock
|
||||||
|
color: ShellSettings.settings.colors["inverse_surface"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
bar/BatteryIndicator.qml
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.UPower
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
implicitWidth: 22
|
||||||
|
implicitHeight: 22
|
||||||
|
visible: UPower.displayDevice.isLaptopBattery
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: root.width
|
||||||
|
height: root.height
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: IconImage {
|
||||||
|
implicitSize: root.width
|
||||||
|
source: "root:resources/battery/battery.svg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: batteryBackground
|
||||||
|
color: Qt.color(ShellSettings.settings.colors["surface"]).lighter(4)
|
||||||
|
opacity: 0.75
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: batteryPercentage
|
||||||
|
width: (parent.width - 4) * UPower.displayDevice.percentage
|
||||||
|
color: ShellSettings.settings.colors["inverse_surface"]
|
||||||
|
anchors {
|
||||||
|
left: batteryBackground.left
|
||||||
|
top: batteryBackground.top
|
||||||
|
bottom: batteryBackground.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
bar/Clock.qml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Text {
|
||||||
|
property string ap: sysClock.hours >= 12 ? "PM" : "AM"
|
||||||
|
property string minutes: sysClock.minutes.toString().padStart(2, '0')
|
||||||
|
property string hours: {
|
||||||
|
var value = sysClock.hours % 12;
|
||||||
|
if (value === 0)
|
||||||
|
return 12;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemClock {
|
||||||
|
id: sysClock
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
text: `${hours}:${minutes} ${ap}`
|
||||||
|
font.pointSize: 11
|
||||||
|
}
|
||||||
66
bar/HyprWorkspaces.qml
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
property var sortedWorkspaces: {
|
||||||
|
let values = Hyprland.workspaces.values.slice();
|
||||||
|
values.sort(function (a, b) {
|
||||||
|
return a.id - b.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: 6
|
||||||
|
visible: Hyprland.monitors.values.length != 0
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: parent.sortedWorkspaces
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
radius: height / 2
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredHeight: 12
|
||||||
|
Layout.preferredWidth: {
|
||||||
|
if (Hyprland.focusedMonitor.activeWorkspace.id == modelData.id)
|
||||||
|
return 25;
|
||||||
|
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
color: {
|
||||||
|
let value = Qt.color(ShellSettings.settings.colors["secondary"]).darker(2);
|
||||||
|
|
||||||
|
if (!modelData?.id || !Hyprland.focusedMonitor?.activeWorkspace?.id)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
if (workspaceButton.containsMouse) {
|
||||||
|
value = ShellSettings.settings.colors["on_primary"];
|
||||||
|
} else if (Hyprland.focusedMonitor.activeWorkspace.id == modelData.id) {
|
||||||
|
value = ShellSettings.settings.colors["primary"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on Layout.preferredWidth {
|
||||||
|
SmoothedAnimation {
|
||||||
|
duration: 150
|
||||||
|
velocity: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: workspaceButton
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onPressed: Hyprland.dispatch(`workspace ${parent.modelData.id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
bar/control/Button.qml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import "../../widgets/" as Widgets
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
id: root
|
||||||
|
required property var bar
|
||||||
|
required property var screen
|
||||||
|
|
||||||
|
implicitSize: 20
|
||||||
|
source: "root:/resources/general/nixos.svg"
|
||||||
|
padding: 2
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (controlPanel.visible) {
|
||||||
|
controlPanel.hide();
|
||||||
|
} else {
|
||||||
|
controlPanel.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlPanel {
|
||||||
|
id: controlPanel
|
||||||
|
|
||||||
|
anchor {
|
||||||
|
window: root.screen
|
||||||
|
|
||||||
|
onAnchoring: {
|
||||||
|
anchor.rect = mapToItem(root.screen.contentItem, 0, root.screen.height, width, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
308
bar/control/ControlPanel.qml
Normal file
|
|
@ -0,0 +1,308 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import "volume" as Volume
|
||||||
|
import "../../widgets/" as Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
// Change to PopupWindow
|
||||||
|
PopupWindow {
|
||||||
|
id: root
|
||||||
|
implicitWidth: 400
|
||||||
|
implicitHeight: container.height + 10
|
||||||
|
color: "transparent"
|
||||||
|
visible: container.opacity > 0
|
||||||
|
|
||||||
|
anchor.rect.x: 0
|
||||||
|
anchor.rect.y: parentWindow.implicitHeight
|
||||||
|
|
||||||
|
// anchors {
|
||||||
|
// top: true
|
||||||
|
// left: true
|
||||||
|
// }
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
container.opacity = 1;
|
||||||
|
grab.active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
container.opacity = 0;
|
||||||
|
grab.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
id: grab
|
||||||
|
windows: [root]
|
||||||
|
onCleared: {
|
||||||
|
root.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add drop shadow effect
|
||||||
|
// Rectangle {
|
||||||
|
// id: shadowSource
|
||||||
|
// color: ShellSettings.settings.colors["surface"]
|
||||||
|
// radius: 8
|
||||||
|
// opacity: container.opacity
|
||||||
|
// width: container.width
|
||||||
|
// height: container.height
|
||||||
|
//
|
||||||
|
// anchors {
|
||||||
|
// top: parent.top
|
||||||
|
// left: parent.left
|
||||||
|
// margins: 5
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// layer.enabled: true
|
||||||
|
// layer.effect: DropShadow {
|
||||||
|
// horizontalOffset: 0
|
||||||
|
// verticalOffset: 2
|
||||||
|
// radius: 8.0
|
||||||
|
// samples: 17
|
||||||
|
// color: Qt.rgba(0, 0, 0, 0.5)
|
||||||
|
// transparentBorder: true
|
||||||
|
// }
|
||||||
|
// visible: false // Hide the source rectangle
|
||||||
|
// }
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: shadowItem
|
||||||
|
anchors.fill: container
|
||||||
|
z: container.z - 1
|
||||||
|
opacity: container.opacity
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: shadowRect
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
horizontalOffset: 0
|
||||||
|
verticalOffset: 2
|
||||||
|
radius: 8.0
|
||||||
|
samples: 17
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.5)
|
||||||
|
source: container
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: container
|
||||||
|
color: ShellSettings.settings.colors["surface"]
|
||||||
|
radius: 18
|
||||||
|
opacity: 0
|
||||||
|
width: parent.width - 10
|
||||||
|
height: contentColumn.implicitHeight + 20
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
margins: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: contentColumn
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// RowLayout {
|
||||||
|
// Layout.fillWidth: true
|
||||||
|
// Layout.preferredHeight: 40
|
||||||
|
//
|
||||||
|
// Rectangle {
|
||||||
|
// radius: 20
|
||||||
|
// color: ShellSettings.settings.colors["surface_container_high"]
|
||||||
|
// Layout.fillWidth: true
|
||||||
|
// Layout.fillHeight: true
|
||||||
|
//
|
||||||
|
// RowLayout {
|
||||||
|
// anchors {
|
||||||
|
// fill: parent
|
||||||
|
// leftMargin: 6
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ProfileImage {
|
||||||
|
// id: profileImage
|
||||||
|
// Layout.preferredWidth: 25
|
||||||
|
// Layout.preferredHeight: 25
|
||||||
|
// // implicitWidth: 30
|
||||||
|
// // implicitHeight: 30
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Text {
|
||||||
|
// text: "kossLAN"
|
||||||
|
// color: ShellSettings.settings.colors["inverse_surface"]
|
||||||
|
// font.pointSize: 12
|
||||||
|
// verticalAlignment: Text.AlignVCenter
|
||||||
|
// Layout.fillWidth: true
|
||||||
|
// Layout.fillHeight: true
|
||||||
|
// Layout.margins: 4
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Rectangle {
|
||||||
|
// radius: 20
|
||||||
|
// color: ShellSettings.settings.colors["surface_container_high"]
|
||||||
|
// Layout.preferredWidth: powerButtons.implicitWidth + 10
|
||||||
|
// Layout.fillHeight: true
|
||||||
|
//
|
||||||
|
// RowLayout {
|
||||||
|
// id: powerButtons
|
||||||
|
// spacing: 10
|
||||||
|
//
|
||||||
|
// anchors {
|
||||||
|
// fill: parent
|
||||||
|
// leftMargin: 5
|
||||||
|
// rightMargin: 5
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Widgets.IconButton {
|
||||||
|
// id: sleepButton
|
||||||
|
// implicitSize: 24
|
||||||
|
// radius: 20
|
||||||
|
// source: "root:resources/control/sleep.svg"
|
||||||
|
// onClicked: sleepProcess.running = true
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Process {
|
||||||
|
// id: sleepProcess
|
||||||
|
// running: false
|
||||||
|
// command: ["hyprctl", "dispatch", "dpms", "off"]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Rectangle {
|
||||||
|
// radius: 20
|
||||||
|
// color: ShellSettings.settings.colors["surface_bright"]
|
||||||
|
// Layout.preferredWidth: 2
|
||||||
|
// Layout.fillHeight: true
|
||||||
|
// Layout.topMargin: 4
|
||||||
|
// Layout.bottomMargin: 4
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Widgets.IconButton {
|
||||||
|
// id: powerButton
|
||||||
|
// implicitSize: 24
|
||||||
|
// radius: 20
|
||||||
|
// source: "root:resources/control/shutdown.svg"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellSettings.settings.colors["surface_container_high"]
|
||||||
|
radius: 12
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [1, 2, 3, 4, 5]
|
||||||
|
delegate: Rectangle {
|
||||||
|
color: ShellSettings.settings.colors["surface_container_high"]
|
||||||
|
radius: width / 2
|
||||||
|
Layout.preferredWidth: 45
|
||||||
|
Layout.preferredHeight: 45
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 55
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellSettings.settings.colors["primary"]
|
||||||
|
radius: width / 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellSettings.settings.colors["primary"]
|
||||||
|
radius: width / 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 55
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellSettings.settings.colors["surface_container_high"]
|
||||||
|
radius: width / 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellSettings.settings.colors["surface_container_high"]
|
||||||
|
radius: width / 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Volume.Mixer {
|
||||||
|
id: sinkMixer
|
||||||
|
isSink: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Volume.Mixer {
|
||||||
|
id: sourceMixer
|
||||||
|
isSink: false
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaPlayer {
|
||||||
|
player: Mpris.players?.values[0]
|
||||||
|
visible: Mpris.players?.values.length != 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 150
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
248
bar/control/MediaPlayer.qml
Normal file
|
|
@ -0,0 +1,248 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import "../.."
|
||||||
|
import "../../widgets" as Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
required property var player
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: root.width
|
||||||
|
height: root.height
|
||||||
|
radius: 14
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorQuantizer {
|
||||||
|
id: gradientQuantizer
|
||||||
|
source: root.player?.trackArtUrl ?? ""
|
||||||
|
depth: 2
|
||||||
|
rescaleSize: 64
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorQuantizer {
|
||||||
|
id: accentQuantizer
|
||||||
|
source: root.player?.trackArtUrl ?? ""
|
||||||
|
depth: 0
|
||||||
|
rescaleSize: 64
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderEffect {
|
||||||
|
property color topLeftColor: gradientQuantizer?.colors[0] ?? "white"
|
||||||
|
property color topRightColor: gradientQuantizer?.colors[1] ?? "black"
|
||||||
|
property color bottomLeftColor: gradientQuantizer?.colors[2] ?? "white"
|
||||||
|
property color bottomRightColor: gradientQuantizer?.colors[3] ?? "black"
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
fragmentShader: "root:/shaders/vertexgradient.frag.qsb"
|
||||||
|
vertexShader: "root:/shaders/vertexgradient.vert.qsb"
|
||||||
|
|
||||||
|
Behavior on topLeftColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on topRightColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on bottomLeftColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on bottomRightColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: cardLayout
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Rectangle {
|
||||||
|
id: mprisImage
|
||||||
|
color: "transparent"
|
||||||
|
radius: 10
|
||||||
|
width: 50
|
||||||
|
height: 50
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
visible: true
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true
|
||||||
|
spread: 0.02
|
||||||
|
samples: 25
|
||||||
|
color: "#80000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: root.player?.trackArtUrl ?? ""
|
||||||
|
sourceSize.width: 1024
|
||||||
|
sourceSize.height: 1024
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: mprisImage.width
|
||||||
|
height: mprisImage.height
|
||||||
|
radius: 10
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: mprisImage.width
|
||||||
|
height: mprisImage.height
|
||||||
|
radius: 10
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.leftMargin: 7.5
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.player?.trackArtist ?? "NA"
|
||||||
|
color: "white"
|
||||||
|
font.pointSize: 13
|
||||||
|
font.bold: true
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.player?.trackTitle ?? "NA"
|
||||||
|
color: "white"
|
||||||
|
font.pointSize: 13
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: timeStr(root.player?.position)
|
||||||
|
color: "white"
|
||||||
|
|
||||||
|
font {
|
||||||
|
pointSize: 9
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameAnimation {
|
||||||
|
running: root.player?.playbackState == MprisPlaybackState.Playing
|
||||||
|
onTriggered: root.player?.positionChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.RoundSlider {
|
||||||
|
id: positionSlider
|
||||||
|
implicitHeight: 7
|
||||||
|
from: 0
|
||||||
|
to: root.player?.length
|
||||||
|
accentColor: accentQuantizer.colors[0]?.darker(1.2) ?? "purple"
|
||||||
|
value: root.player?.position ?? 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
onMoved: {
|
||||||
|
if (root.player == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
root.player.position = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: timeStr(root.player?.length)
|
||||||
|
color: "white"
|
||||||
|
|
||||||
|
font {
|
||||||
|
pointSize: 9
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Music Controls
|
||||||
|
RowLayout {
|
||||||
|
spacing: 2
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
implicitSize: 40
|
||||||
|
activeRectangle: false
|
||||||
|
padding: 4
|
||||||
|
source: "root:resources/mpris/previous.svg"
|
||||||
|
onClicked: root.player?.previous()
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
implicitSize: 40
|
||||||
|
activeRectangle: false
|
||||||
|
padding: 4
|
||||||
|
source: root.player?.isPlaying ? "root:resources/mpris/pause.svg" : "root:resources/mpris/play.svg"
|
||||||
|
onClicked: {
|
||||||
|
if (!root.player?.canPlay)
|
||||||
|
return;
|
||||||
|
player.isPlaying ? player.pause() : player.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
implicitSize: 40
|
||||||
|
activeRectangle: false
|
||||||
|
padding: 4
|
||||||
|
source: "root:resources/mpris/next.svg"
|
||||||
|
onClicked: root.player?.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeStr(time: int): string {
|
||||||
|
const seconds = time % 60;
|
||||||
|
const minutes = Math.floor(time / 60);
|
||||||
|
|
||||||
|
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
bar/control/ProfileImage.qml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: profileImage
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: "root:resources/general/pfp.png"
|
||||||
|
sourceSize.width: 100
|
||||||
|
sourceSize.height: 100
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: profileImage.width
|
||||||
|
height: profileImage.height
|
||||||
|
radius: 10
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: profileImage.width
|
||||||
|
height: profileImage.height
|
||||||
|
radius: 10
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
79
bar/control/volume/Card.qml
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
import "../../.."
|
||||||
|
import "../../../widgets" as Widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
required property PwNode node
|
||||||
|
required property var isSink
|
||||||
|
color: ShellSettings.settings.colors["surface_container_high"]
|
||||||
|
|
||||||
|
PwObjectTracker {
|
||||||
|
id: defaultSourceTracker
|
||||||
|
objects: [root.node]
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.leftMargin: 2
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Text {
|
||||||
|
color: ShellSettings.settings.colors["inverse_surface"]
|
||||||
|
text: {
|
||||||
|
|
||||||
|
// Taken from quickshell-examples
|
||||||
|
const app = root.node?.properties["application.name"] ?? (root.node?.description != "" ? root.node?.description : root.node?.name);
|
||||||
|
const media = root.node?.properties["media.name"];
|
||||||
|
const title = media != undefined ? `${app} - ${media}` : app;
|
||||||
|
|
||||||
|
return title != undefined ? title : "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
font.bold: true
|
||||||
|
|
||||||
|
elide: Text.ElideRight
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 5
|
||||||
|
Layout.rightMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.RoundSlider {
|
||||||
|
implicitHeight: 7
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
value: root.node?.audio.volume ?? 0
|
||||||
|
onValueChanged: root.node.audio.volume = value
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.bottomMargin: 7.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
source: {
|
||||||
|
if (!root.isSink)
|
||||||
|
return root.node?.audio.muted ? "root:resources/volume/microphone-mute.svg" : "root:resources/volume/microphone-full.svg";
|
||||||
|
|
||||||
|
return root.node?.audio.muted ? "root:resources/volume/volume-mute.svg" : "root:resources/volume/volume-full.svg";
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitSize: 36
|
||||||
|
padding: 4
|
||||||
|
radius: implicitSize / 2
|
||||||
|
Layout.rightMargin: 10
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.node.audio.muted = !root.node.audio.muted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
176
bar/control/volume/Mixer.qml
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
import "../../../widgets/" as Widgets
|
||||||
|
import "../../.."
|
||||||
|
|
||||||
|
// TODO: refactor this trash
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
required property var isSink
|
||||||
|
color: "transparent"
|
||||||
|
radius: 10
|
||||||
|
|
||||||
|
property bool expanded: false
|
||||||
|
property int baseHeight: 60
|
||||||
|
property int contentHeight: expanded ? (applicationVolumes.count * baseHeight) : 0
|
||||||
|
|
||||||
|
implicitHeight: baseHeight + contentHeight
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: root.width
|
||||||
|
height: root.height
|
||||||
|
radius: root.baseHeight / 2
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: headerSection
|
||||||
|
width: parent.width
|
||||||
|
height: root.baseHeight
|
||||||
|
anchors.top: parent.top
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 0
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellSettings.settings.colors["surface_container_high"]
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
id: arrowButton
|
||||||
|
implicitSize: 44
|
||||||
|
activeRectangle: false
|
||||||
|
source: "root:resources/general/right-arrow.svg"
|
||||||
|
padding: 4
|
||||||
|
rotation: root.expanded ? 90 : 0
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
Behavior on rotation {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.expanded = !root.expanded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.preferredWidth: 40
|
||||||
|
Layout.preferredHeight: root.baseHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
Card {
|
||||||
|
node: root.isSink ? Pipewire.defaultAudioSink : Pipewire.defaultAudioSource
|
||||||
|
isSink: root.isSink
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: root.baseHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: divider
|
||||||
|
color: ShellSettings.settings.colors["surface_bright"]
|
||||||
|
height: 2
|
||||||
|
width: parent.width
|
||||||
|
anchors.top: headerSection.bottom
|
||||||
|
|
||||||
|
opacity: root.expanded ? 1.0 : 0.0
|
||||||
|
|
||||||
|
// Behavior on opacity {
|
||||||
|
// NumberAnimation {
|
||||||
|
// duration: 150
|
||||||
|
// easing.type: Easing.OutCubic
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contentSection
|
||||||
|
width: parent.width
|
||||||
|
anchors.top: divider.bottom
|
||||||
|
height: root.contentHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
// Behavior on height {
|
||||||
|
// SmoothedAnimation {
|
||||||
|
// duration: 150
|
||||||
|
// velocity: 200
|
||||||
|
// easing.type: Easing.OutCubic
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: applicationsColumn
|
||||||
|
width: parent.width
|
||||||
|
anchors.top: parent.top
|
||||||
|
opacity: root.expanded ? 1.0 : 0.0
|
||||||
|
|
||||||
|
// Behavior on opacity {
|
||||||
|
// NumberAnimation {
|
||||||
|
// duration: 100
|
||||||
|
// easing.type: Easing.OutCubic
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
PwNodeLinkTracker {
|
||||||
|
id: linkTracker
|
||||||
|
node: root.isSink ? Pipewire.defaultAudioSink : Pipewire.defaultAudioSource
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: applicationVolumes
|
||||||
|
model: linkTracker.linkGroups
|
||||||
|
|
||||||
|
delegate: RowLayout {
|
||||||
|
id: cardRow
|
||||||
|
required property PwLinkGroup modelData
|
||||||
|
spacing: 0
|
||||||
|
width: applicationsColumn.width
|
||||||
|
height: root.baseHeight
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellSettings.settings.colors["surface_container_high"]
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
implicitSize: 32
|
||||||
|
source: {
|
||||||
|
if (cardRow.modelData.source?.properties["application.icon-name"] == null) {
|
||||||
|
return "root:resources/general/placeholder.svg";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `image://icon/${cardRow.modelData.source?.properties["application.icon-name"]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
leftMargin: 8
|
||||||
|
rightMargin: 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.preferredWidth: 40
|
||||||
|
Layout.preferredHeight: root.baseHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
Card {
|
||||||
|
node: cardRow.modelData.source
|
||||||
|
isSink: root.isSink
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: root.baseHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
177
bar/mpris/Card.qml
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import "../.."
|
||||||
|
import "../../widgets" as Widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var player
|
||||||
|
|
||||||
|
radius: 5
|
||||||
|
color: "transparent"
|
||||||
|
implicitHeight: 220
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: cardLayout
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
leftMargin: 10
|
||||||
|
rightMargin: 10
|
||||||
|
topMargin: 10 // Added top margin for better spacing
|
||||||
|
bottomMargin: 10 // Added bottom margin for better spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mprisImage
|
||||||
|
color: "transparent"
|
||||||
|
radius: 10
|
||||||
|
width: 200
|
||||||
|
height: 200
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
visible: true
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: player.trackArtUrl
|
||||||
|
sourceSize.width: 1024
|
||||||
|
sourceSize.height: 1024
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: mprisImage.width
|
||||||
|
height: mprisImage.height
|
||||||
|
radius: 10
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: mprisImage.width
|
||||||
|
height: mprisImage.height
|
||||||
|
radius: 10
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true
|
||||||
|
spread: 0.02
|
||||||
|
samples: 25
|
||||||
|
color: "#80000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: player.trackArtist
|
||||||
|
color: "white"
|
||||||
|
font.pointSize: 13
|
||||||
|
font.bold: true
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: player.trackTitle
|
||||||
|
color: "white"
|
||||||
|
font.pointSize: 13
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
ColorQuantizer {
|
||||||
|
id: colorQuantizer
|
||||||
|
source: Qt.resolvedUrl(Media.trackedPlayer?.trackArtUrl ?? "")
|
||||||
|
depth: 0
|
||||||
|
rescaleSize: 64
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: timeStr(player.position)
|
||||||
|
color: "white"
|
||||||
|
|
||||||
|
font {
|
||||||
|
pointSize: 9
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.RoundSlider {
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
accentColor: colorQuantizer.colors[0]
|
||||||
|
//value: root.node.audio.volume
|
||||||
|
//onValueChanged: node.audio.volume = value
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 16
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: timeStr(player.length)
|
||||||
|
color: "white"
|
||||||
|
|
||||||
|
font {
|
||||||
|
pointSize: 9
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Music Controls
|
||||||
|
RowLayout {
|
||||||
|
spacing: 2
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
implicitSize: 36
|
||||||
|
activeRectangle: false
|
||||||
|
padding: 4
|
||||||
|
source: "root:resources/mpris/previous.svg"
|
||||||
|
onClicked: player.previous()
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
implicitSize: 36
|
||||||
|
activeRectangle: false
|
||||||
|
padding: 4
|
||||||
|
source: player?.isPlaying ? "root:resources/mpris/pause.svg" : "root:resources/mpris/play.svg"
|
||||||
|
onClicked: {
|
||||||
|
if (!player.canPlay)
|
||||||
|
return;
|
||||||
|
player.isPlaying ? player.pause() : player.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
implicitSize: 36
|
||||||
|
activeRectangle: false
|
||||||
|
padding: 4
|
||||||
|
source: "root:resources/mpris/next.svg"
|
||||||
|
onClicked: player.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeStr(time: int): string {
|
||||||
|
const seconds = time % 60;
|
||||||
|
const minutes = Math.floor(time / 60);
|
||||||
|
|
||||||
|
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
46
bar/mpris/Media.qml
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
property MprisPlayer trackedPlayer
|
||||||
|
property var colors: ["white"]
|
||||||
|
|
||||||
|
Instantiator {
|
||||||
|
model: Mpris.players
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
required property MprisPlayer modelData
|
||||||
|
target: modelData
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (root.trackedPlayer == null || modelData.isPlaying) {
|
||||||
|
root.trackedPlayer = modelData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
if (root.trackedPlayer == null || !root.trackedPlayer.isPlaying) {
|
||||||
|
for (const player of Mpris.players.values) {
|
||||||
|
if (player.playbackState.isPlaying) {
|
||||||
|
root.trackedPlayer = player;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedPlayer == null && Mpris.players.values.length != 0) {
|
||||||
|
trackedPlayer = Mpris.players.values[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPlaybackStateChanged() {
|
||||||
|
if (root.trackedPlayer !== modelData)
|
||||||
|
root.trackedPlayer = modelData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
151
bar/mpris/Player.qml
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root
|
||||||
|
width: mediaPlayerContainer.width + 10
|
||||||
|
height: mediaPlayerContainer.height + 10
|
||||||
|
color: "transparent"
|
||||||
|
visible: mediaPlayerContainer.opacity > 0
|
||||||
|
|
||||||
|
anchor.rect.x: parentWindow.width / 2 - width / 2
|
||||||
|
anchor.rect.y: parentWindow.height
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
mediaPlayerContainer.opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
mediaPlayerContainer.opacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hoverHandler
|
||||||
|
enabled: true
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered == false) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mediaPlayerContainer
|
||||||
|
width: 500
|
||||||
|
height: mediaPlayerColumn.height + 20
|
||||||
|
color: ShellGlobals.colors.background
|
||||||
|
radius: 5
|
||||||
|
opacity: 0
|
||||||
|
anchors.centerIn: parent
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: mediaPlayerContainer.width
|
||||||
|
height: mediaPlayerContainer.height
|
||||||
|
radius: mediaPlayerContainer.radius
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: mediaPlayerContainer.width
|
||||||
|
height: mediaPlayerContainer.height
|
||||||
|
radius: mediaPlayerContainer.radius
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
transparentBorder: true
|
||||||
|
spread: 0.02
|
||||||
|
samples: 25
|
||||||
|
color: "#80000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorQuantizer {
|
||||||
|
id: colorQuantizer
|
||||||
|
source: Qt.resolvedUrl(Media.trackedPlayer?.trackArtUrl ?? "")
|
||||||
|
depth: 2
|
||||||
|
rescaleSize: 64
|
||||||
|
|
||||||
|
onColorsChanged: {
|
||||||
|
Media.colors = colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderEffect {
|
||||||
|
property color topLeftColor: colorQuantizer?.colors[0]?.lighter(1.2) ?? "white"
|
||||||
|
property color topRightColor: colorQuantizer?.colors[1]?.lighter(1.2) ?? "black"
|
||||||
|
property color bottomLeftColor: colorQuantizer?.colors[2]?.lighter(1.2) ?? "white"
|
||||||
|
property color bottomRightColor: colorQuantizer?.colors[3]?.lighter(1.2) ?? "black"
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
fragmentShader: "root:/shaders/vertexgradient.frag.qsb"
|
||||||
|
vertexShader: "root:/shaders/vertexgradient.vert.qsb"
|
||||||
|
|
||||||
|
Behavior on topLeftColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on topRightColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on bottomLeftColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on bottomRightColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mediaPlayerColumn
|
||||||
|
spacing: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredWidth: parent.width
|
||||||
|
Layout.margins: 10
|
||||||
|
implicitHeight: childrenRect.height
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// Media Cards
|
||||||
|
Repeater {
|
||||||
|
model: Mpris.players
|
||||||
|
|
||||||
|
Card {
|
||||||
|
required property var modelData
|
||||||
|
player: modelData
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
bar/mpris/Status.qml
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
required property var bar
|
||||||
|
|
||||||
|
width: statusInfo.width + 125
|
||||||
|
height: parent.height
|
||||||
|
visible: Mpris.players.values.length != 0
|
||||||
|
|
||||||
|
Player {
|
||||||
|
id: mediaPlayer
|
||||||
|
anchor.window: bar
|
||||||
|
anchor.rect.x: parentWindow.width / 2 - width / 2
|
||||||
|
anchor.rect.y: parentWindow.height
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: playButton
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: mouse => {
|
||||||
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
if (mediaPlayer.visible) {
|
||||||
|
mediaPlayer.hide();
|
||||||
|
} else {
|
||||||
|
mediaPlayer.show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!Media.trackedPlayer.canPlay || Media.trackedPlayer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Media.trackedPlayer.isPlaying)
|
||||||
|
Media.trackedPlayer.pause();
|
||||||
|
else
|
||||||
|
Media.trackedPlayer.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: statusInfo
|
||||||
|
width: statusIcon.width + statusIcon.anchors.rightMargin + nowPlayingText.width
|
||||||
|
height: parent.height
|
||||||
|
visible: Media.trackedPlayer != null
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.botton
|
||||||
|
margins: 3.5
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: statusIcon
|
||||||
|
implicitSize: 13
|
||||||
|
source: Media.trackedPlayer?.isPlaying ? "root:resources/mpris/pause.svg" : "root:resources/mpris/play.svg"
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
right: nowPlayingText.left
|
||||||
|
rightMargin: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: nowPlayingText
|
||||||
|
color: ShellGlobals.colors.text
|
||||||
|
text: `${Media.trackedPlayer?.trackArtist} - ${Media.trackedPlayer?.trackTitle}`
|
||||||
|
font.pointSize: 11
|
||||||
|
width: Math.min(implicitWidth, 250)
|
||||||
|
elide: Text.ElideRight
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
bar/notifications/NotificationButton.qml
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
required property var bar
|
||||||
|
property var implicitSize: 0
|
||||||
|
readonly property real actualSize: Math.min(root.width, root.height)
|
||||||
|
|
||||||
|
implicitWidth: parent.height
|
||||||
|
implicitHeight: parent.height
|
||||||
|
|
||||||
|
NotificationCenter {
|
||||||
|
id: notificationCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: mouseArea.containsMouse ? ShellSettings.settings.colors["primary"] : "transparent"
|
||||||
|
radius: 5
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: {
|
||||||
|
if (notificationCenter.visible) {
|
||||||
|
notificationCenter.hide();
|
||||||
|
} else {
|
||||||
|
notificationCenter.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
implicitWidth: root.implicitSize
|
||||||
|
implicitHeight: root.implicitSize
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: root.actualSize
|
||||||
|
height: root.actualSize
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: IconImage {
|
||||||
|
implicitSize: root.actualSize
|
||||||
|
source: "root:resources/general/notification.svg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: mouseArea.containsMouse ? ShellSettings.settings.colors["inverse_primary"] : ShellSettings.settings.colors["inverse_surface"]
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: notification number overlay
|
||||||
|
}
|
||||||
161
bar/popups/MenuWindow.qml
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root
|
||||||
|
color: "transparent"
|
||||||
|
implicitWidth: bar.width
|
||||||
|
implicitHeight: Math.max(popupContainer.height, 800) + 20
|
||||||
|
|
||||||
|
mask: Region {
|
||||||
|
item: popupContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
anchor {
|
||||||
|
window: bar
|
||||||
|
rect: Qt.rect(0, 0, bar.width, bar.height)
|
||||||
|
edges: Edges.Bottom | Edges.Left
|
||||||
|
gravity: Edges.Bottom | Edges.Right
|
||||||
|
adjustment: PopupAdjustment.None
|
||||||
|
}
|
||||||
|
|
||||||
|
required property var bar
|
||||||
|
|
||||||
|
function set(item, content) {
|
||||||
|
content.visible = true;
|
||||||
|
|
||||||
|
let itemPos = item.mapToItem(bar.contentItem, 0, bar.height, item.width, 0).x;
|
||||||
|
// let contentWidth = content.width;
|
||||||
|
|
||||||
|
popupContainer.x = itemPos;
|
||||||
|
popupContent.data = content;
|
||||||
|
|
||||||
|
// popupContent.opacity = 0;
|
||||||
|
// popupContainer.opacity = 0;
|
||||||
|
popupContainer.opacity = 1;
|
||||||
|
popupContent.opacity = 1;
|
||||||
|
root.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// function set(item, content) {
|
||||||
|
// content.visible = true;
|
||||||
|
//
|
||||||
|
// let itemPos = item.mapToItem(bar.contentItem, 0, bar.height, item.width, 0).x;
|
||||||
|
// let contentWidth = content.width;
|
||||||
|
// let padding = 5;
|
||||||
|
// let xPos = itemPos;
|
||||||
|
// let idealX = xPos;
|
||||||
|
// let idealRightEdge = idealX + contentWidth;
|
||||||
|
//
|
||||||
|
// // check if touching right edge
|
||||||
|
// let maxRightEdge = root.width - padding;
|
||||||
|
// let isTouchingRightEdge = idealRightEdge > maxRightEdge;
|
||||||
|
//
|
||||||
|
// if (isTouchingRightEdge) {
|
||||||
|
// // touching right edge
|
||||||
|
// let constrainedX = maxRightEdge - contentWidth;
|
||||||
|
// constrainedX = Math.max(0, constrainedX);
|
||||||
|
//
|
||||||
|
// popupContainer.x = constrainedX;
|
||||||
|
// popupContainer.implicitWidth = 0;
|
||||||
|
// popupContent.data = content;
|
||||||
|
// // popupContent.implicitWidth = contentWidth;
|
||||||
|
// } else {
|
||||||
|
// // not touching any edge
|
||||||
|
// // popupContent.implicitWidth = contentWidth;
|
||||||
|
// popupContainer.x = idealX;
|
||||||
|
// popupContent.data = content;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// popupContainer.y = padding;
|
||||||
|
//
|
||||||
|
// popupContent.opacity = 0;
|
||||||
|
// popupContainer.opacity = 0;
|
||||||
|
// popupContainer.opacity = 1;
|
||||||
|
// popupContent.opacity = 1;
|
||||||
|
// root.visible = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
function clear() {
|
||||||
|
popupContainer.opacity = 0;
|
||||||
|
popupContent.opacity = 0;
|
||||||
|
popupContent.data = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapperRectangle {
|
||||||
|
id: popupContainer
|
||||||
|
property real targetX: 0
|
||||||
|
|
||||||
|
color: ShellSettings.settings.colors["surface"]
|
||||||
|
radius: 12
|
||||||
|
margin: 8
|
||||||
|
clip: true
|
||||||
|
opacity: 0
|
||||||
|
visible: opacity > 0
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (!visible) {
|
||||||
|
root.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: popupContent
|
||||||
|
implicitWidth: Math.max(childrenRect.width, 120)
|
||||||
|
implicitHeight: Math.max(childrenRect.height, 60)
|
||||||
|
opacity: 1
|
||||||
|
|
||||||
|
// Behavior on opacity {
|
||||||
|
// NumberAnimation {
|
||||||
|
// id: contentOpacity
|
||||||
|
// duration: 350
|
||||||
|
// easing.type: Easing.Linear
|
||||||
|
// from: 0
|
||||||
|
// to: 1
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hover
|
||||||
|
enabled: true
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered == false)
|
||||||
|
root.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Behavior on opacity {
|
||||||
|
// NumberAnimation {
|
||||||
|
// duration: 500
|
||||||
|
// easing.type: Easing.InOutQuad
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Behavior on x {
|
||||||
|
// enabled: root.visible
|
||||||
|
// SmoothedAnimation {
|
||||||
|
// duration: 300
|
||||||
|
// easing.type: Easing.OutQuad
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Behavior on implicitWidth {
|
||||||
|
// enabled: root.visible
|
||||||
|
// SmoothedAnimation {
|
||||||
|
// duration: 300
|
||||||
|
// easing.type: Easing.OutQuad
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Behavior on implicitHeight {
|
||||||
|
// SmoothedAnimation {
|
||||||
|
// duration: 200
|
||||||
|
// easing.type: Easing.Linear
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
9
bar/systray/SubTrayMenu.qml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
visible: false
|
||||||
|
implicitWidth: 20
|
||||||
|
implicitHeight: 20
|
||||||
|
}
|
||||||
97
bar/systray/SysTray.qml
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.SystemTray
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: root
|
||||||
|
spacing: 10
|
||||||
|
visible: SystemTray.items.values.length > 0
|
||||||
|
implicitHeight: parent.height
|
||||||
|
|
||||||
|
required property var popup
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: SystemTray.items
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
id: trayField
|
||||||
|
implicitHeight: parent.height
|
||||||
|
implicitWidth: trayContainer.width
|
||||||
|
required property SystemTrayItem modelData
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: trayButton
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
// trayText.width = sysTrayContent.width - trayIcon.width - trayContainer.spacing;
|
||||||
|
// trayText.visible = true;
|
||||||
|
root.popup.set(this, trayMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QsMenuOpener {
|
||||||
|
id: menuOpener
|
||||||
|
menu: trayField.modelData.menu
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapperItem {
|
||||||
|
id: trayMenu
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: menuContainer
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: menuOpener.children
|
||||||
|
|
||||||
|
delegate: TrayMenu {
|
||||||
|
id: sysTrayContent
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: trayContainer
|
||||||
|
color: trayButton.containsMouse ? ShellSettings.settings.colors["primary"] : "transparent"
|
||||||
|
radius: width / 2
|
||||||
|
implicitHeight: parent.height - 2
|
||||||
|
implicitWidth: parent.height - 2
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: trayIcon
|
||||||
|
|
||||||
|
source: {
|
||||||
|
switch (trayField.modelData.id) {
|
||||||
|
case "obs":
|
||||||
|
return "image://icon/obs-tray";
|
||||||
|
default:
|
||||||
|
return trayField.modelData.icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
bar/systray/TrayMenu.qml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
required property QsMenuEntry modelData
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: (root.modelData?.isSeparator ?? false)
|
||||||
|
color: ShellSettings.settings.colors["surface_container_high"]
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 2
|
||||||
|
Layout.leftMargin: 8
|
||||||
|
Layout.rightMargin: 8
|
||||||
|
}
|
||||||
|
|
||||||
|
TrayMenuEntry {
|
||||||
|
visible: !root.modelData?.isSeparator
|
||||||
|
menuData: root.modelData
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
135
bar/systray/TrayMenuEntry.qml
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
required property var menuData
|
||||||
|
|
||||||
|
WrapperRectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 25
|
||||||
|
radius: 6
|
||||||
|
color: {
|
||||||
|
if (!root.menuData?.enabled)
|
||||||
|
return "transparent";
|
||||||
|
|
||||||
|
if (entryArea.containsMouse)
|
||||||
|
return ShellSettings.settings.colors["primary"];
|
||||||
|
|
||||||
|
return "transparent";
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapperMouseArea {
|
||||||
|
id: entryArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
if (!root.menuData?.enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (root.menuData?.hasChildren)
|
||||||
|
subTrayMenu.visible = !subTrayMenu.visible;
|
||||||
|
|
||||||
|
root.menuData?.triggered();
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: menuEntry
|
||||||
|
spacing: 5
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 20
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.leftMargin: 5
|
||||||
|
|
||||||
|
RadioButton {
|
||||||
|
id: radioButton
|
||||||
|
visible: (root.menuData?.buttonType === QsMenuButtonType.RadioButton) ?? false
|
||||||
|
checked: (root.menuData?.checkState) ?? false
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: checkBox
|
||||||
|
visible: (root.menuData?.buttonType === QsMenuButtonType.CheckBox) ?? false
|
||||||
|
checked: (root.menuData?.checkState) ?? false
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: entryImage
|
||||||
|
visible: (root.menuData?.buttonType === QsMenuButtonType.None && root.menuData?.icon !== "") ?? false
|
||||||
|
source: (root.menuData?.icon) ?? ""
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: text
|
||||||
|
text: (root.menuData?.text) ?? ""
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: {
|
||||||
|
let color = Qt.color(ShellSettings.settings.colors["inverse_surface"]);
|
||||||
|
|
||||||
|
if (!root.menuData?.enabled)
|
||||||
|
return color.darker(2);
|
||||||
|
|
||||||
|
if (entryArea.containsMouse)
|
||||||
|
return Qt.color(ShellSettings.settings.colors["inverse_primary"]);
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredHeight: 20
|
||||||
|
Layout.preferredWidth: 20
|
||||||
|
Layout.rightMargin: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapperRectangle {
|
||||||
|
id: subTrayMenu
|
||||||
|
color: ShellSettings.settings.colors["surface_container"]
|
||||||
|
radius: 8
|
||||||
|
visible: false
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
QsMenuOpener {
|
||||||
|
id: menuOpener
|
||||||
|
menu: root.menuData
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: subTrayContainer
|
||||||
|
spacing: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: menuOpener.children
|
||||||
|
|
||||||
|
delegate: BoundComponent {
|
||||||
|
id: subMenuEntry
|
||||||
|
source: "TrayMenu.qml"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
required property var modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
313
launcher/Controller.qml
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
PersistentProperties {
|
||||||
|
id: persist
|
||||||
|
property bool launcherOpen: false
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "launcher"
|
||||||
|
|
||||||
|
function open(): void {
|
||||||
|
persist.launcherOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): void {
|
||||||
|
persist.launcherOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(): void {
|
||||||
|
persist.launcherOpen = !persist.launcherOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: loader
|
||||||
|
activeAsync: persist.launcherOpen
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
implicitWidth: 500
|
||||||
|
implicitHeight: 7 + searchContainer.implicitHeight + list.topMargin * 2 + list.delegateHeight * 10
|
||||||
|
color: "transparent"
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||||
|
WlrLayershell.namespace: "shell:launcher"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: container
|
||||||
|
color: ShellSettings.settings.colors["surface"]
|
||||||
|
radius: 18
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 7
|
||||||
|
anchors.topMargin: 10
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: searchContainer
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: searchbox.implicitHeight + 15
|
||||||
|
radius: 10
|
||||||
|
color: ShellSettings.settings.colors["surface_container"]
|
||||||
|
border.color: ShellSettings.settings.colors["secondary"]
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: searchbox
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 5
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: search
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: ShellSettings.settings.colors["inverse_surface"]
|
||||||
|
|
||||||
|
focus: true
|
||||||
|
Keys.forwardTo: [list]
|
||||||
|
Keys.onEscapePressed: persist.launcherOpen = false
|
||||||
|
|
||||||
|
Keys.onPressed: event => {
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (event.key == Qt.Key_J) {
|
||||||
|
list.currentIndex = list.currentIndex == list.count - 1 ? 0 : list.currentIndex + 1;
|
||||||
|
event.accepted = true;
|
||||||
|
} else if (event.key == Qt.Key_K) {
|
||||||
|
list.currentIndex = list.currentIndex == 0 ? list.count - 1 : list.currentIndex - 1;
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
if (list.currentItem) {
|
||||||
|
list.currentItem.clicked(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
list.currentIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: list
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
clip: true
|
||||||
|
cacheBuffer: 0 // works around QTBUG-131106
|
||||||
|
//reuseItems: true
|
||||||
|
model: ScriptModel {
|
||||||
|
values: DesktopEntries.applications.values.map(object => {
|
||||||
|
const stxt = search.text.toLowerCase();
|
||||||
|
const ntxt = object.name.toLowerCase();
|
||||||
|
let si = 0;
|
||||||
|
let ni = 0;
|
||||||
|
|
||||||
|
let matches = [];
|
||||||
|
let startMatch = -1;
|
||||||
|
|
||||||
|
for (let si = 0; si != stxt.length; ++si) {
|
||||||
|
const sc = stxt[si];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Drop any entries with letters that don't exist in order
|
||||||
|
if (ni == ntxt.length)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const nc = ntxt[ni++];
|
||||||
|
|
||||||
|
if (nc == sc) {
|
||||||
|
if (startMatch == -1)
|
||||||
|
startMatch = ni;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (startMatch != -1) {
|
||||||
|
matches.push({
|
||||||
|
index: startMatch,
|
||||||
|
length: ni - startMatch
|
||||||
|
});
|
||||||
|
|
||||||
|
startMatch = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startMatch != -1) {
|
||||||
|
matches.push({
|
||||||
|
index: startMatch,
|
||||||
|
length: ni - startMatch + 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
object: object,
|
||||||
|
matches: matches
|
||||||
|
};
|
||||||
|
}).filter(entry => entry !== null).sort((a, b) => {
|
||||||
|
let ai = 0;
|
||||||
|
let bi = 0;
|
||||||
|
let s = 0;
|
||||||
|
|
||||||
|
while (ai != a.matches.length && bi != b.matches.length) {
|
||||||
|
const am = a.matches[ai];
|
||||||
|
const bm = b.matches[bi];
|
||||||
|
|
||||||
|
s = bm.length - am.length;
|
||||||
|
if (s != 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
s = am.index - bm.index;
|
||||||
|
if (s != 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
++ai;
|
||||||
|
++bi;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = a.matches.length - b.matches.length;
|
||||||
|
if (s != 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
s = a.object.name.length - b.object.name.length;
|
||||||
|
if (s != 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
return a.object.name.localeCompare(b.object.name);
|
||||||
|
}).map(entry => entry.object)
|
||||||
|
|
||||||
|
onValuesChanged: list.currentIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
topMargin: 7
|
||||||
|
bottomMargin: list.count == 0 ? 0 : 7
|
||||||
|
|
||||||
|
add: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displaced: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "y"
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
to: 1
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
move: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "y"
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
to: 1
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "y"
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
to: 0
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
highlight: Rectangle {
|
||||||
|
radius: 12
|
||||||
|
color: ShellSettings.settings.colors["primary"]
|
||||||
|
}
|
||||||
|
|
||||||
|
keyNavigationEnabled: true
|
||||||
|
keyNavigationWraps: true
|
||||||
|
highlightMoveVelocity: -1
|
||||||
|
highlightMoveDuration: 100
|
||||||
|
preferredHighlightBegin: list.topMargin
|
||||||
|
preferredHighlightEnd: list.height - list.bottomMargin
|
||||||
|
highlightRangeMode: ListView.ApplyRange
|
||||||
|
snapMode: ListView.SnapToItem
|
||||||
|
|
||||||
|
readonly property real delegateHeight: 44
|
||||||
|
|
||||||
|
delegate: MouseArea {
|
||||||
|
required property DesktopEntry modelData
|
||||||
|
|
||||||
|
implicitHeight: list.delegateHeight
|
||||||
|
implicitWidth: ListView.view.width
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
modelData.execute();
|
||||||
|
persist.launcherOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: delegateLayout
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
asynchronous: true
|
||||||
|
implicitSize: 30
|
||||||
|
source: Quickshell.iconPath(modelData.icon)
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
text: modelData.name
|
||||||
|
color: ShellSettings.settings.colors["inverse_surface"]
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
}
|
||||||
|
}
|
||||||
218
notifications/ActiveToast.qml
Normal file
|
|
@ -0,0 +1,218 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import "../widgets/" as Widgets
|
||||||
|
import ".."
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
required property var notification
|
||||||
|
signal expired(Notification notification)
|
||||||
|
signal closed(Notification notification)
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: Math.min(row.implicitHeight + 30, 400)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: container
|
||||||
|
radius: 10
|
||||||
|
color: ShellSettings.settings.colors["surface_container"]
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: timerController
|
||||||
|
property int totalDuration: 5000
|
||||||
|
property int remainingTime: totalDuration
|
||||||
|
property bool isRunning: false
|
||||||
|
property real lastTime: 0
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: internalTimer
|
||||||
|
interval: 16
|
||||||
|
repeat: true
|
||||||
|
running: timerController.isRunning
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
var currentTime = Date.now();
|
||||||
|
if (timerController.lastTime > 0) {
|
||||||
|
var delta = currentTime - timerController.lastTime;
|
||||||
|
timerController.remainingTime -= delta;
|
||||||
|
if (timerController.remainingTime <= 0) {
|
||||||
|
timerController.isRunning = false;
|
||||||
|
root.expired(root.notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timerController.lastTime = currentTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
if (!isRunning) {
|
||||||
|
lastTime = Date.now();
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pause() {
|
||||||
|
isRunning = false;
|
||||||
|
lastTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: notificationArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onContainsMouseChanged: {
|
||||||
|
progressAnimation.paused = containsMouse;
|
||||||
|
if (containsMouse) {
|
||||||
|
timerController.pause();
|
||||||
|
} else {
|
||||||
|
timerController.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: row
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: topRow
|
||||||
|
spacing: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
visible: root.notification.appIcon != ""
|
||||||
|
source: Quickshell.iconPath(root.notification.appIcon)
|
||||||
|
implicitSize: 24
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: appName
|
||||||
|
text: root.notification.appName
|
||||||
|
color: ShellSettings.settings.colors["inverse_surface"]
|
||||||
|
font.pointSize: 11
|
||||||
|
font.bold: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
Layout.preferredWidth: implicitWidth
|
||||||
|
Layout.maximumWidth: topRow.width * 0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.Separator {}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: summaryText
|
||||||
|
text: root.notification.summary
|
||||||
|
color: ShellSettings.settings.colors["inverse_surface"]
|
||||||
|
font.pointSize: 11
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: closeButton
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: progressCircle
|
||||||
|
anchors.fill: parent
|
||||||
|
antialiasing: true
|
||||||
|
|
||||||
|
property real progress: 1.0
|
||||||
|
onProgressChanged: requestPaint()
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
ctx.reset();
|
||||||
|
|
||||||
|
var centerX = width / 2;
|
||||||
|
var centerY = height / 2;
|
||||||
|
var radius = Math.min(width, height) / 2 - 2;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + 2 * Math.PI * progress);
|
||||||
|
ctx.strokeStyle = ShellSettings.settings.colors["primary"];
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: progressAnimation
|
||||||
|
target: progressCircle
|
||||||
|
property: "progress"
|
||||||
|
from: 1.0
|
||||||
|
to: 0.0
|
||||||
|
duration: 5000
|
||||||
|
running: true
|
||||||
|
easing.type: Easing.Linear
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: closeButtonBg
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
color: "#FF474D"
|
||||||
|
radius: 10
|
||||||
|
visible: closeButtonArea.containsMouse
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: closeButtonArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: root.closed(root.notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
source: "image://icon/window-close"
|
||||||
|
implicitSize: 16
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: bodyText
|
||||||
|
text: root.notification.body
|
||||||
|
color: ShellSettings.settings.colors["inverse_surface"]
|
||||||
|
font.pointSize: 11
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
98
notifications/Controller.qml
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Notifications.notificationServer
|
||||||
|
|
||||||
|
function onNotification(notification) {
|
||||||
|
notificationLoader.item.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: notificationLoader
|
||||||
|
loading: true
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: notificationWindow
|
||||||
|
property var visibleCount: {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < toastList.count; i++) {
|
||||||
|
let item = toastList.itemAt(i);
|
||||||
|
|
||||||
|
if (item && item.visible) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
onVisibleCountChanged: visible = visibleCount != 0
|
||||||
|
|
||||||
|
color: "transparent"
|
||||||
|
implicitWidth: 525
|
||||||
|
visible: false
|
||||||
|
exclusionMode: ExclusionMode.Normal
|
||||||
|
|
||||||
|
mask: Region {
|
||||||
|
item: notifLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
bottom: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "length: " + notificationWindow.visibleCount
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: notifLayout
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: toastList
|
||||||
|
model: ScriptModel {
|
||||||
|
values: Notifications.notificationServer.trackedNotifications.values.concat()
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ActiveToast {
|
||||||
|
id: toast
|
||||||
|
required property var modelData
|
||||||
|
notification: modelData
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: toast
|
||||||
|
|
||||||
|
function onExpired(notification) {
|
||||||
|
toast.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClosed(notification) {
|
||||||
|
notification.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
278
notifications/NotificationCenter.qml
Normal file
|
|
@ -0,0 +1,278 @@
|
||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../widgets" as Widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
PersistentProperties {
|
||||||
|
id: persist
|
||||||
|
property bool notificationsOpen: false
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
id: ipc
|
||||||
|
target: "notifications"
|
||||||
|
|
||||||
|
function open(): void {
|
||||||
|
persist.notificationsOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): void {
|
||||||
|
persist.notificationsOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(): void {
|
||||||
|
persist.notificationsOpen = !persist.notificationsOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: loader
|
||||||
|
activeAsync: persist.notificationsOpen
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: notificationPanel
|
||||||
|
color: "red"
|
||||||
|
implicitWidth: 500
|
||||||
|
exclusionMode: ExclusionMode.Normal
|
||||||
|
// WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
right: true
|
||||||
|
bottom: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Notifications: " + toastList.count
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: toastList
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
clip: true
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
model: ScriptModel {
|
||||||
|
values: {
|
||||||
|
const notifications = Notifications.notificationServer.trackedNotifications.values.concat();
|
||||||
|
|
||||||
|
const groupedByApp = notifications.reduce((groups, notification) => {
|
||||||
|
const appName = notification.appName;
|
||||||
|
|
||||||
|
if (!groups[appName]) {
|
||||||
|
groups[appName] = {
|
||||||
|
appName: appName,
|
||||||
|
summaryGroups: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const summary = notification.summary;
|
||||||
|
const image = notification.image;
|
||||||
|
|
||||||
|
if (!groups[appName].summaryGroups[summary]) {
|
||||||
|
groups[appName].summaryGroups[summary] = {
|
||||||
|
summary: summary,
|
||||||
|
image: image,
|
||||||
|
notifications: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
groups[appName].summaryGroups[summary].notifications.push(notification);
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return Object.values(groupedByApp).map(appGroup => {
|
||||||
|
return {
|
||||||
|
appName: appGroup.appName,
|
||||||
|
summaryGroups: Object.values(appGroup.summaryGroups)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
id: toastWrapper
|
||||||
|
required property var modelData
|
||||||
|
width: ListView.view.width
|
||||||
|
height: toastContent.height
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: toastContent
|
||||||
|
width: parent.width
|
||||||
|
height: contentColumn.implicitHeight
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: contentColumn
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification content
|
||||||
|
Repeater {
|
||||||
|
model: toastWrapper.modelData.summaryGroups
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: summaryGroup
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: groupContent.implicitHeight + 24
|
||||||
|
color: ShellSettings.settings.colors["surface_container"]
|
||||||
|
antialiasing: true
|
||||||
|
|
||||||
|
topLeftRadius: index === 0 ? 25 : 5
|
||||||
|
topRightRadius: index === 0 ? 25 : 5
|
||||||
|
bottomLeftRadius: index === (toastWrapper.modelData.summaryGroups.length - 1) ? 25 : 5
|
||||||
|
bottomRightRadius: index === (toastWrapper.modelData.summaryGroups.length - 1) ? 25 : 5
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: groupContent
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 12
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: imageContainer
|
||||||
|
Layout.preferredWidth: 36
|
||||||
|
Layout.preferredHeight: 36
|
||||||
|
visible: summaryGroup.modelData.image != ""
|
||||||
|
antialiasing: true
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: notificationImage
|
||||||
|
anchors.fill: parent
|
||||||
|
source: summaryGroup.modelData.image
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: notificationImage.width
|
||||||
|
height: notificationImage.height
|
||||||
|
radius: notificationImage.width / 2
|
||||||
|
antialiasing: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content column
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
// Header row
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: summaryGroup.modelData.summary
|
||||||
|
font.pixelSize: 16
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: ShellSettings.settings.colors["on_surface"]
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
maximumLineCount: 2
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.Separator {}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "now"
|
||||||
|
font.pixelSize: 14
|
||||||
|
color: ShellSettings.settings.colors["on_surface_variant"]
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification bodies
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: summaryGroup.modelData.notifications
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
id: bodyDelegate
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Text {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: bodyDelegate.modelData.body
|
||||||
|
font.pixelSize: 14
|
||||||
|
color: ShellSettings.settings.colors["on_surface_variant"]
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
maximumLineCount: 4
|
||||||
|
elide: Text.ElideRight
|
||||||
|
lineHeight: 1.3
|
||||||
|
visible: bodyDelegate.modelData.body != ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HyprlanFocusGrab {
|
||||||
|
// id: grab
|
||||||
|
// windows: [notificationPanel]
|
||||||
|
// onCleared: {
|
||||||
|
// ipc.hide();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
}
|
||||||
|
}
|
||||||
23
notifications/Notifications.qml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
property alias notificationServer: notifServer
|
||||||
|
|
||||||
|
NotificationServer {
|
||||||
|
id: notifServer
|
||||||
|
actionsSupported: true
|
||||||
|
persistenceSupported: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: notifServer
|
||||||
|
|
||||||
|
function onNotification(notification) {
|
||||||
|
notification.tracked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
resources/battery/battery-charge.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M24,0V24H0V0Z" fill="none"/>
|
||||||
|
<path d="M20,10V8.33A1.34,1.34,0,0,0,18.67,7H3.34A1.34,1.34,0,0,0,2,8.33v7.33A1.34,1.34,0,0,0,3.33,17H18.67A1.34,1.34,0,0,0,20,15.67V14h2V10Zm-8.5,3v2L4,11H9.5V9L17,13Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 448 B |
6
resources/battery/battery.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M20,15.67V14h2V10H20V8.33A1.34,1.34,0,0,0,18.67,7H3.34A1.34,1.34,0,0,0,2,8.33v7.33A1.34,1.34,0,0,0,3.33,17H18.67A1.34,1.34,0,0,0,20,15.67Z"/>
|
||||||
|
<path d="M20,15.67V14h2V10H20V8.33A1.34,1.34,0,0,0,18.67,7H3.34A1.34,1.34,0,0,0,2,8.33v7.33A1.34,1.34,0,0,0,3.33,17H18.67A1.34,1.34,0,0,0,20,15.67Z" fill="white"/>
|
||||||
|
<path d="M24,0V24H0V0Z" fill="none"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 581 B |
7
resources/control/controls-button.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="64px" viewBox="0 0 16 16" width="64px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="white">
|
||||||
|
<path d="m 4.550781 1 c -1.9375 0 -3.5 1.5625 -3.5 3.5 s 1.5625 3.5 3.5 3.5 h 7 c 1.941407 0 3.5 -1.5625 3.5 -3.5 s -1.558593 -3.5 -3.5 -3.5 z m 7 1 c 1.386719 0 2.5 1.113281 2.5 2.5 c 0 1.382812 -1.113281 2.5 -2.5 2.5 c -1.382812 0 -2.5 -1.117188 -2.5 -2.5 c 0 -1.386719 1.117188 -2.5 2.5 -2.5 z m 0 0"/>
|
||||||
|
<path d="m 4.550781 9 c -1.9375 0 -3.5 1.5625 -3.5 3.5 s 1.5625 3.5 3.5 3.5 h 7 c 1.941407 0 3.5 -1.5625 3.5 -3.5 s -1.558593 -3.5 -3.5 -3.5 z m 0 1 c 1.386719 0 2.5 1.113281 2.5 2.5 c 0 1.382812 -1.113281 2.5 -2.5 2.5 c -1.382812 0 -2.5 -1.117188 -2.5 -2.5 c 0 -1.386719 1.117188 -2.5 2.5 -2.5 z m 0 0" fill="white"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 805 B |