init: working version

This commit is contained in:
Kiana Sheibani 2025-10-07 19:43:46 -04:00
commit 7d8d7dacae
Signed by: toki
GPG key ID: 6CB106C25E86A9F7
109 changed files with 15066 additions and 0 deletions

View file

@ -0,0 +1,29 @@
pragma ComponentBehavior: Bound
import qs.services
import qs.config
import qs.custom
import QtQuick
import QtQuick.Layouts
ColumnLayout {
anchors.centerIn: parent
anchors.verticalCenterOffset: -2
spacing: -6
CustomText {
Layout.alignment: Qt.AlignHCenter
text: Time.format("hh:mm:ss")
color: Config.colors.secondary
font.family: Config.font.family.mono
font.pointSize: Config.font.size.largest
font.weight: 600
}
CustomText {
Layout.alignment: Qt.AlignHCenter
text: Time.format("dddd, yyyy-MM-dd")
color: Config.colors.tertiary
font.pointSize: Config.font.size.normal
}
}

View file

@ -0,0 +1,243 @@
import qs.config
import qs.custom
import qs.services
import qs.util
import QtQuick
import QtQuick.Shapes
Item {
id: root
anchors.fill: parent
property real playerProgress: {
const active = Players.active;
return active?.length ? active.position / active.length : 0;
}
Behavior on playerProgress {
Anim {
duration: Config.anim.durations.large
}
}
Timer {
running: Players.active?.isPlaying ?? false
interval: 400
triggeredOnStart: true
repeat: true
onTriggered: Players.active?.positionChanged()
}
Shape {
id: progress
preferredRendererType: Shape.CurveRenderer
readonly property int thickness: 8
readonly property int angle: 300
ShapePath {
id: path
fillColor: "transparent"
strokeColor: Qt.alpha(Config.colors.media, 0.2)
strokeWidth: progress.thickness
capStyle: ShapePath.RoundCap
PathAngleArc {
centerX: cover.x + cover.width / 2
centerY: cover.y + cover.height / 2
radiusX: (cover.width + progress.thickness) / 2 + 7
radiusY: (cover.height + progress.thickness) / 2 + 7
startAngle: -90 - progress.angle / 2
sweepAngle: progress.angle
}
Behavior on strokeColor {
CAnim {}
}
}
ShapePath {
fillColor: "transparent"
strokeColor: Config.colors.media
strokeWidth: progress.thickness
capStyle: ShapePath.RoundCap
PathAngleArc {
centerX: cover.x + cover.width / 2
centerY: cover.y + cover.height / 2
radiusX: (cover.width + progress.thickness) / 2 + 7
radiusY: (cover.height + progress.thickness) / 2 + 7
startAngle: -90 - progress.angle / 2
// NOTE: Cap progress angle to account for bad MPRIS players
sweepAngle: progress.angle * Math.min(root.playerProgress, 1)
}
Behavior on strokeColor {
CAnim {}
}
}
}
CustomClippingRect {
id: cover
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 18 + progress.thickness
implicitHeight: width
color: Config.colors.inactive
radius: Infinity
MaterialIcon {
anchors.centerIn: parent
grade: 200
text: "art_track"
color: Config.colors.tertiary
font.pointSize: (parent.width * 0.4) || 1
}
Image {
id: image
anchors.fill: parent
source: Players.active?.trackArtUrl ?? ""
asynchronous: true
fillMode: Image.PreserveAspectCrop
sourceSize.width: width
sourceSize.height: height
}
}
CustomText {
id: title
anchors.top: cover.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: album.text === "" ? 24 : 18
anchors.leftMargin: 15
anchors.rightMargin: 15
animate: true
horizontalAlignment: Text.AlignHCenter
text: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title")
color: Config.colors.secondary
font.pointSize: Config.font.size.normal
elide: Text.ElideRight
}
CustomText {
id: artist
anchors.top: title.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 15
anchors.rightMargin: 15
animate: true
horizontalAlignment: Text.AlignHCenter
text: (Players.active?.trackArtist ?? qsTr("No media")) || qsTr("Unknown artist")
opacity: Players.active ? 1 : 0
color: Config.colors.primary
elide: Text.ElideRight
}
CustomText {
id: album
anchors.top: artist.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 15
anchors.rightMargin: 15
animate: true
horizontalAlignment: Text.AlignHCenter
text: Players.active?.trackAlbum ?? ""
opacity: Players.active ? 1 : 0
color: Config.colors.tertiary
font.pointSize: Config.font.size.small
elide: Text.ElideRight
}
Row {
id: controls
anchors.top: album.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: album.text === "" ? -4 : 2
spacing: 7
Control {
icon: "skip_previous"
canUse: Players.active?.canGoPrevious ?? false
function onClicked(): void {
Players.active?.previous();
}
}
Control {
icon: Players.active?.isPlaying ? "pause" : "play_arrow"
canUse: Players.active?.canTogglePlaying ?? false
function onClicked(): void {
Players.active?.togglePlaying();
}
}
Control {
icon: "skip_next"
canUse: Players.active?.canGoNext ?? false
function onClicked(): void {
Players.active?.next();
}
}
}
component Control: CustomRect {
id: control
required property string icon
required property bool canUse
function onClicked(): void {
}
implicitWidth: Math.max(icon.implicitHeight, icon.implicitHeight) + 12
implicitHeight: implicitWidth
StateLayer {
anchors.fill: parent
disabled: !control.canUse
radius: 1000
function onClicked(): void {
control.onClicked();
}
}
MaterialIcon {
id: icon
anchors.centerIn: parent
anchors.verticalCenterOffset: font.pointSize * 0.05
animate: true
text: control.icon
color: control.canUse ? Config.colors.media : Config.colors.inactive
font.pointSize: Config.font.size.large
}
}
}

View file

@ -0,0 +1,264 @@
pragma ComponentBehavior: Bound
import qs.config
import qs.custom
import qs.services
import qs.util
import Quickshell
import Quickshell.Services.Notifications
import QtQuick
import QtQuick.Layouts
import qs.modules.notifications as N
ColumnLayout {
id: root
anchors.fill: parent
spacing: 7
readonly property int notifCount:
(Notifs.list && Notifs.list.length !== undefined) ? Notifs.list.length :
((Notifs.list && Notifs.list.count !== undefined) ? Notifs.list.count : 0)
function notifAt(i) {
if (!Notifs.list)
return undefined;
if (typeof Notifs.list.get === 'function')
return Notifs.list.get(i);
return Notifs.list[i];
}
function scrollToTop(): void {
if (notifScroll && notifScroll.contentItem && notifScroll.contentItem.contentY !== undefined) {
notifScroll.contentItem.contentY = 0;
}
}
RowLayout {
Layout.alignment: Qt.AlignTop
Layout.margins: 10
Layout.fillWidth: true
spacing: 7
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
id: icon
text: {
if (Notifs.dnd)
return "notifications_off";
if (notifCount > 0)
return "notifications_active";
return "notifications";
}
fill: {
if (Notifs.dnd)
return 0;
if (notifCount > 0)
return 1;
return 0;
}
color: Notifs.dnd ? Config.colors.error : Config.colors.notification
font.pointSize: Config.font.size.larger
animate: true
Behavior on color {
SequentialAnimation {
PauseAnimation {
duration: icon.animateDuration / 2
}
PropertyAction {}
}
}
}
CustomText {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
text: notifCount > 0 ? qsTr("%1 notifications").arg(notifCount) : qsTr("No notifications")
font.weight: 600
font.pointSize: Config.font.size.normal
animate: true
}
CustomText {
Layout.alignment: Qt.AlignVCenter
text: qsTr("Do Not Disturb")
}
CustomSwitch {
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: 5
Layout.rightMargin: 5
bg: Config.colors.containerAlt
accent: Color.mute(Config.colors.notification)
checked: Notifs.dnd
onToggled: Notifs.toggleDnd()
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: 10
Layout.rightMargin: 10
Layout.bottomMargin: 10
CustomListView {
id: notifScroll
anchors.fill: parent
anchors.margins: 10
anchors.rightMargin: 5
spacing: 12
clip: true
CustomScrollBar.vertical: CustomScrollBar {
flickable: notifScroll
}
model: ScriptModel {
values: [...Notifs.list].reverse()
}
delegate: Item {
id: wrapper
required property int index
required property var modelData
readonly property alias nonAnimHeight: notif.nonAnimHeight
width: 405
height: notif.implicitHeight
ListView.onRemove: removeAnim.start()
SequentialAnimation {
id: removeAnim
PropertyAction {
target: wrapper
property: "ListView.delayRemove"
value: true
}
PropertyAction {
target: wrapper
property: "enabled"
value: false
}
PropertyAction {
target: wrapper
property: "implicitHeight"
value: 0
}
PropertyAction {
target: wrapper
property: "z"
value: 1
}
Anim {
target: notif
property: "x"
to: (notif.x >= 0 ? Config.notifs.width : -Config.notifs.width) * 2
easing.bezierCurve: Config.anim.curves.emphasized
}
PropertyAction {
target: wrapper
property: "ListView.delayRemove"
value: false
}
}
N.Notification {
id: notif
width: parent.width
notif: wrapper.modelData
color: wrapper.modelData.urgency === NotificationUrgency.Critical ? Config.colors.errorBg : Config.colors.containerAlt
inPopup: false
}
}
add: Transition {
Anim {
property: "x"
from: Config.notifs.width
to: 0
easing.bezierCurve: Config.anim.curves.emphasizedDecel
}
}
move: Transition {
NotifAnim {
property: "y"
}
}
displaced: Transition {
NotifAnim {
property: "y"
}
}
}
}
Item {
Layout.alignment: Qt.AlignBottom
Layout.fillWidth: true
height: 0
CustomRect {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: bottomMargin
property real bottomMargin: notifCount > 0 ? 12 : -4
opacity: notifCount > 0 ? 1 : 0
visible: opacity > 0
implicitWidth: clearBtn.implicitWidth + 32
implicitHeight: clearBtn.implicitHeight + 20
radius: 25
color: Config.colors.inactive
Behavior on opacity {
Anim {}
}
Behavior on bottomMargin {
Anim {}
}
StateLayer {
anchors.fill: parent
function onClicked(): void {
for (let i = root.notifCount - 1; i >= 0; i--) {
const n = root.notifAt(i);
n?.notification?.dismiss();
}
}
}
RowLayout {
id: clearBtn
anchors.centerIn: parent
spacing: 7
MaterialIcon {
id: clearIcon
text: "clear_all"
color: Config.colors.secondary
}
CustomText {
text: qsTr("Clear all")
color: Config.colors.secondary
}
}
}
}
component NotifAnim: Anim {
duration: Config.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Config.anim.curves.expressiveDefaultSpatial
}
}

View file

@ -0,0 +1,73 @@
import qs.config
import qs.custom
import qs.services
import qs.util
import Quickshell
import QtQuick
Column {
id: root
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 24
Item {
id: userLine
implicitWidth: childrenRect.width
implicitHeight: childrenRect.height
MaterialIcon {
id: userIcon
anchors.left: parent.left
text: "account_circle"
fill: 1
color: Config.colors.primary
font.pointSize: Config.font.size.larger
}
CustomText {
id: userText
anchors.verticalCenter: userIcon.verticalCenter
anchors.left: userIcon.right
anchors.leftMargin: 7
text: qsTr("User: %1").arg(User.user)
color: Config.colors.secondary
}
}
Item {
id: uptimeLine
implicitWidth: childrenRect.width
implicitHeight: childrenRect.height
MaterialIcon {
id: uptimeIcon
anchors.left: parent.left
text: "timer"
fill: 1
color: Config.colors.primary
font.pointSize: Config.font.size.larger
}
CustomText {
id: uptimeText
anchors.verticalCenter: uptimeIcon.verticalCenter
anchors.left: uptimeIcon.right
anchors.verticalCenterOffset: 1
anchors.leftMargin: 7
text: qsTr("Uptime: %1").arg(User.uptime)
color: Config.colors.secondary
}
}
}

View file

@ -0,0 +1,129 @@
import qs.config
import qs.custom
import qs.services
import qs.util
import QtQuick
import QtQuick.Layouts
Item {
id: root
anchors.fill: parent
anchors.margins: 16
anchors.topMargin: 6
Component.onCompleted: Weather.reload()
CustomText {
id: city
anchors.top: parent.top
anchors.left: parent.left
text: Weather.city
color: Config.colors.tertiary
}
MaterialIcon {
id: icon
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: font.pointSize * 0.25
animate: true
animateProp: "opacity"
text: Weather.icon
color: Weather.iconColor
font.pointSize: Config.font.size.largest * 1.8
Behavior on color {
SequentialAnimation {
PauseAnimation {
duration: icon.animateDuration / 2
}
PropertyAction {}
}
}
}
Column {
id: info
anchors.left: icon.right
anchors.verticalCenter: icon.verticalCenter
anchors.verticalCenterOffset: -3
anchors.leftMargin: 14
spacing: 1
opacity: Weather.available ? 1 : 0
Behavior on opacity {
SequentialAnimation {
PauseAnimation {
duration: temp.animateDuration / 2
}
PropertyAction {}
}
}
CustomText {
id: temp
animate: true
text: Weather.temp
color: Config.colors.primary
font.pointSize: Config.font.size.large
font.weight: 500
// Reduce padding at bottom of text
height: implicitHeight * 0.9
CustomText {
anchors.left: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: 12
anchors.bottomMargin: 1
animate: true
text: Weather.feelsLike
color: Config.colors.tertiary
font.pointSize: Config.font.size.larger
}
}
CustomText {
animate: true
text: Weather.description
color: Config.colors.secondary
elide: Text.ElideRight
width: Math.min(implicitWidth, root.parent.width - icon.implicitWidth - info.anchors.leftMargin - 30)
}
Item {
implicitWidth: childrenRect.width
implicitHeight: childrenRect.height
MaterialIcon {
id: humidityIcon
animate: true
text: Weather.humidityIcon
color: Config.colors.primary
font.pointSize: Config.font.size.normal
}
CustomText {
anchors.left: humidityIcon.right
anchors.verticalCenter: humidityIcon.verticalCenter
anchors.leftMargin: 2
animate: true
text: `${Math.round(Weather.humidity * 100)}% Humidity`
color: Config.colors.primary
}
}
}
}