formatter

This commit is contained in:
Zacharias-Brohn
2026-02-24 23:20:11 +01:00
parent 40cd984b6d
commit d56a0260fb
202 changed files with 15037 additions and 15352 deletions
+3 -3
View File
@@ -2,7 +2,7 @@ import QtQuick
import qs.Config
NumberAnimation {
duration: MaterialEasing.standardTime
easing.type: Easing.BezierSpline
easing.bezierCurve: MaterialEasing.standard
duration: MaterialEasing.standardTime
easing.bezierCurve: MaterialEasing.standard
easing.type: Easing.BezierSpline
}
+3 -3
View File
@@ -2,7 +2,7 @@ import QtQuick
import qs.Config
ColorAnimation {
duration: MaterialEasing.standardTime
easing.type: Easing.BezierSpline
easing.bezierCurve: MaterialEasing.standard
duration: MaterialEasing.standardTime
easing.bezierCurve: MaterialEasing.standard
easing.type: Easing.BezierSpline
}
+84 -88
View File
@@ -4,103 +4,99 @@ import QtQuick
import QtQuick.Templates
BusyIndicator {
id: root
id: root
enum AnimType {
Advance = 0,
Retreat
}
enum AnimState {
Stopped,
Running,
Completing
}
enum AnimType {
Advance = 0,
Retreat
}
enum AnimState {
Stopped,
Running,
Completing
}
property int animState
property color bgColour: DynamicColors.palette.m3secondaryContainer
property color fgColour: DynamicColors.palette.m3primary
property real implicitSize: Appearance.font.size.normal * 3
property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress
property real strokeWidth: Appearance.padding.small * 0.8
property alias type: manager.indeterminateAnimationType
property real implicitSize: Appearance.font.size.normal * 3
property real strokeWidth: Appearance.padding.small * 0.8
property color fgColour: DynamicColors.palette.m3primary
property color bgColour: DynamicColors.palette.m3secondaryContainer
implicitHeight: implicitSize
implicitWidth: implicitSize
padding: 0
property alias type: manager.indeterminateAnimationType
readonly property alias progress: manager.progress
contentItem: CircularProgress {
anchors.fill: parent
bgColour: root.bgColour
fgColour: root.fgColour
padding: root.padding
rotation: manager.rotation
startAngle: manager.startFraction * 360
strokeWidth: root.internalStrokeWidth
value: manager.endFraction - manager.startFraction
}
states: State {
name: "stopped"
when: !root.running
property real internalStrokeWidth: strokeWidth
property int animState
PropertyChanges {
root.internalStrokeWidth: root.strokeWidth / 3
root.opacity: 0
}
}
transitions: Transition {
Anim {
duration: manager.completeEndDuration * Appearance.anim.durations.scale
properties: "opacity,internalStrokeWidth"
}
}
padding: 0
implicitWidth: implicitSize
implicitHeight: implicitSize
Component.onCompleted: {
if (running) {
running = false;
running = true;
}
}
onRunningChanged: {
if (running) {
manager.completeEndProgress = 0;
animState = CircularIndicator.Running;
} else {
if (animState == CircularIndicator.Running)
animState = CircularIndicator.Completing;
}
}
Component.onCompleted: {
if (running) {
running = false;
running = true;
}
}
CircularIndicatorManager {
id: manager
onRunningChanged: {
if (running) {
manager.completeEndProgress = 0;
animState = CircularIndicator.Running;
} else {
if (animState == CircularIndicator.Running)
animState = CircularIndicator.Completing;
}
}
}
states: State {
name: "stopped"
when: !root.running
NumberAnimation {
duration: manager.duration * Appearance.anim.durations.scale
from: 0
loops: Animation.Infinite
property: "progress"
running: root.animState !== CircularIndicator.Stopped
target: manager
to: 1
}
PropertyChanges {
root.opacity: 0
root.internalStrokeWidth: root.strokeWidth / 3
}
}
NumberAnimation {
duration: manager.completeEndDuration * Appearance.anim.durations.scale
from: 0
property: "completeEndProgress"
running: root.animState === CircularIndicator.Completing
target: manager
to: 1
transitions: Transition {
Anim {
properties: "opacity,internalStrokeWidth"
duration: manager.completeEndDuration * Appearance.anim.durations.scale
}
}
contentItem: CircularProgress {
anchors.fill: parent
strokeWidth: root.internalStrokeWidth
fgColour: root.fgColour
bgColour: root.bgColour
padding: root.padding
rotation: manager.rotation
startAngle: manager.startFraction * 360
value: manager.endFraction - manager.startFraction
}
CircularIndicatorManager {
id: manager
}
NumberAnimation {
running: root.animState !== CircularIndicator.Stopped
loops: Animation.Infinite
target: manager
property: "progress"
from: 0
to: 1
duration: manager.duration * Appearance.anim.durations.scale
}
NumberAnimation {
running: root.animState === CircularIndicator.Completing
target: manager
property: "completeEndProgress"
from: 0
to: 1
duration: manager.completeEndDuration * Appearance.anim.durations.scale
onFinished: {
if (root.animState === CircularIndicator.Completing)
root.animState = CircularIndicator.Stopped;
}
}
onFinished: {
if (root.animState === CircularIndicator.Completing)
root.animState = CircularIndicator.Stopped;
}
}
}
+52 -53
View File
@@ -3,65 +3,64 @@ import QtQuick.Shapes
import qs.Config
Shape {
id: root
id: root
property real value
property int startAngle: -90
property int strokeWidth: Appearance.padding.smaller
property int padding: 0
property int spacing: Appearance.spacing.small
property color fgColour: DynamicColors.palette.m3primary
property color bgColour: DynamicColors.palette.m3secondaryContainer
readonly property real arcRadius: (size - padding - strokeWidth) / 2
property color bgColour: DynamicColors.palette.m3secondaryContainer
property color fgColour: DynamicColors.palette.m3primary
readonly property real gapAngle: ((spacing + strokeWidth) / (arcRadius || 1)) * (180 / Math.PI)
property int padding: 0
readonly property real size: Math.min(width, height)
property int spacing: Appearance.spacing.small
property int startAngle: -90
property int strokeWidth: Appearance.padding.smaller
readonly property real vValue: value || 1 / 360
property real value
readonly property real size: Math.min(width, height)
readonly property real arcRadius: (size - padding - strokeWidth) / 2
readonly property real vValue: value || 1 / 360
readonly property real gapAngle: ((spacing + strokeWidth) / (arcRadius || 1)) * (180 / Math.PI)
asynchronous: true
preferredRendererType: Shape.CurveRenderer
preferredRendererType: Shape.CurveRenderer
asynchronous: true
ShapePath {
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent"
strokeColor: root.bgColour
strokeWidth: root.strokeWidth
ShapePath {
fillColor: "transparent"
strokeColor: root.bgColour
strokeWidth: root.strokeWidth
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
Behavior on strokeColor {
CAnim {
duration: Appearance.anim.durations.large
}
}
PathAngleArc {
startAngle: root.startAngle + 360 * root.vValue + root.gapAngle
sweepAngle: Math.max(-root.gapAngle, 360 * (1 - root.vValue) - root.gapAngle * 2)
radiusX: root.arcRadius
radiusY: root.arcRadius
centerX: root.size / 2
centerY: root.size / 2
}
PathAngleArc {
centerX: root.size / 2
centerY: root.size / 2
radiusX: root.arcRadius
radiusY: root.arcRadius
startAngle: root.startAngle + 360 * root.vValue + root.gapAngle
sweepAngle: Math.max(-root.gapAngle, 360 * (1 - root.vValue) - root.gapAngle * 2)
}
}
Behavior on strokeColor {
CAnim {
duration: Appearance.anim.durations.large
}
}
}
ShapePath {
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent"
strokeColor: root.fgColour
strokeWidth: root.strokeWidth
ShapePath {
fillColor: "transparent"
strokeColor: root.fgColour
strokeWidth: root.strokeWidth
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
Behavior on strokeColor {
CAnim {
duration: Appearance.anim.durations.large
}
}
PathAngleArc {
startAngle: root.startAngle
sweepAngle: 360 * root.vValue
radiusX: root.arcRadius
radiusY: root.arcRadius
centerX: root.size / 2
centerY: root.size / 2
}
Behavior on strokeColor {
CAnim {
duration: Appearance.anim.durations.large
}
}
}
PathAngleArc {
centerX: root.size / 2
centerY: root.size / 2
radiusX: root.arcRadius
radiusY: root.arcRadius
startAngle: root.startAngle
sweepAngle: 360 * root.vValue
}
}
}
+20 -21
View File
@@ -5,31 +5,30 @@ import Quickshell.Widgets
import QtQuick
IconImage {
id: root
id: root
required property color color
required property color color
asynchronous: true
asynchronous: true
layer.enabled: true
layer.enabled: true
layer.effect: Coloriser {
sourceColor: analyser.dominantColour
colorizationColor: root.color
}
layer.effect: Coloriser {
colorizationColor: root.color
sourceColor: analyser.dominantColour
}
layer.onEnabledChanged: {
if (layer.enabled && status === Image.Ready)
analyser.requestUpdate();
}
layer.onEnabledChanged: {
if (layer.enabled && status === Image.Ready)
analyser.requestUpdate();
}
onStatusChanged: {
if (layer.enabled && status === Image.Ready)
analyser.requestUpdate();
}
onStatusChanged: {
if (layer.enabled && status === Image.Ready)
analyser.requestUpdate();
}
ImageAnalyser {
id: analyser
ImageAnalyser {
id: analyser
sourceItem: root
}
sourceItem: root
}
}
+7 -6
View File
@@ -2,12 +2,13 @@ import QtQuick
import QtQuick.Effects
MultiEffect {
property color sourceColor: "black"
property color sourceColor: "black"
colorization: 1
brightness: 1 - sourceColor.hslLightness
brightness: 1 - sourceColor.hslLightness
colorization: 1
Behavior on colorizationColor {
CAnim {}
}
Behavior on colorizationColor {
CAnim {
}
}
}
+48 -56
View File
@@ -3,80 +3,72 @@ import QtQuick.Templates
import qs.Config
Slider {
id: root
id: root
required property real peak
property color nonPeakColor: DynamicColors.tPalette.m3primary
required property real peak
property color peakColor: DynamicColors.palette.m3primary
background: Item {
CustomRect {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.topMargin: root.implicitHeight / 3
anchors.bottomMargin: root.implicitHeight / 3
implicitWidth: root.handle.x - root.implicitHeight
color: root.nonPeakColor
radius: 1000
topRightRadius: root.implicitHeight / 15
bottomRightRadius: root.implicitHeight / 15
background: Item {
CustomRect {
anchors.bottom: parent.bottom
anchors.bottomMargin: root.implicitHeight / 3
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: root.implicitHeight / 3
bottomRightRadius: root.implicitHeight / 15
color: root.nonPeakColor
implicitWidth: root.handle.x - root.implicitHeight
radius: 1000
topRightRadius: root.implicitHeight / 15
CustomRect {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
bottomRightRadius: root.implicitHeight / 15
color: root.peakColor
implicitWidth: parent.width * root.peak
radius: 1000
topRightRadius: root.implicitHeight / 15
bottomRightRadius: root.implicitHeight / 15
color: root.peakColor
Behavior on implicitWidth {
Anim { duration: 50 }
Anim {
duration: 50
}
}
}
}
}
CustomRect {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.topMargin: root.implicitHeight / 3
anchors.bottomMargin: root.implicitHeight / 3
implicitWidth: root.implicitWidth - root.handle.x - root.handle.implicitWidth - root.implicitHeight
CustomRect {
anchors.bottom: parent.bottom
anchors.bottomMargin: root.implicitHeight / 3
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: root.implicitHeight / 3
bottomLeftRadius: root.implicitHeight / 15
color: DynamicColors.tPalette.m3surfaceContainer
implicitWidth: root.implicitWidth - root.handle.x - root.handle.implicitWidth - root.implicitHeight
radius: 1000
topLeftRadius: root.implicitHeight / 15
Component.onCompleted: {
console.log(root.handle.x, implicitWidth)
console.log(root.handle.x, implicitWidth);
}
}
}
handle: CustomRect {
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3primary
implicitHeight: 15
implicitWidth: 5
radius: 1000
x: root.visualPosition * root.availableWidth - implicitWidth / 2
color: DynamicColors.tPalette.m3surfaceContainer
radius: 1000
topLeftRadius: root.implicitHeight / 15
bottomLeftRadius: root.implicitHeight / 15
}
}
handle: CustomRect {
x: root.visualPosition * root.availableWidth - implicitWidth / 2
implicitWidth: 5
implicitHeight: 15
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3primary
radius: 1000
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: Qt.PointingHandCursor
}
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
}
}
+55 -51
View File
@@ -2,64 +2,68 @@ import QtQuick
import QtQuick.Controls.Basic
BusyIndicator {
id: control
property color color: delegate.color
id: control
property int busySize: 64
property color color: delegate.color
contentItem: Item {
implicitWidth: control.busySize
implicitHeight: control.busySize
contentItem: Item {
implicitHeight: control.busySize
implicitWidth: control.busySize
Item {
id: item
x: parent.width / 2 - (control.busySize / 2)
y: parent.height / 2 - (control.busySize / 2)
width: control.busySize
height: control.busySize
opacity: control.running ? 1 : 0
Item {
id: item
Behavior on opacity {
OpacityAnimator {
duration: 250
}
}
height: control.busySize
opacity: control.running ? 1 : 0
width: control.busySize
x: parent.width / 2 - (control.busySize / 2)
y: parent.height / 2 - (control.busySize / 2)
RotationAnimator {
target: item
running: control.visible && control.running
from: 0
to: 360
loops: Animation.Infinite
duration: 1250
}
Behavior on opacity {
OpacityAnimator {
duration: 250
}
}
Repeater {
id: repeater
model: 6
RotationAnimator {
duration: 1250
from: 0
loops: Animation.Infinite
running: control.visible && control.running
target: item
to: 360
}
CustomRect {
id: delegate
x: item.width / 2 - width / 2
y: item.height / 2 - height / 2
implicitWidth: 10
implicitHeight: 10
radius: 5
color: control.color
Repeater {
id: repeater
required property int index
model: 6
transform: [
Translate {
y: -Math.min(item.width, item.height) * 0.5 + 5
},
Rotation {
angle: delegate.index / repeater.count * 360
origin.x: 5
origin.y: 5
}
]
}
}
}
}
CustomRect {
id: delegate
required property int index
color: control.color
implicitHeight: 10
implicitWidth: 10
radius: 5
x: item.width / 2 - width / 2
y: item.height / 2 - height / 2
transform: [
Translate {
y: -Math.min(item.width, item.height) * 0.5 + 5
},
Rotation {
angle: delegate.index / repeater.count * 360
origin.x: 5
origin.y: 5
}
]
}
}
}
}
}
+10 -12
View File
@@ -4,30 +4,28 @@ import QtQuick.Controls
Button {
id: control
required property color textColor
required property color bgColor
property int radius: 4
required property color textColor
contentItem: CustomText {
text: control.text
background: CustomRect {
color: control.bgColor
opacity: control.enabled ? 1.0 : 0.5
radius: control.radius
}
contentItem: CustomText {
color: control.textColor
horizontalAlignment: Text.AlignHCenter
opacity: control.enabled ? 1.0 : 0.5
text: control.text
verticalAlignment: Text.AlignVCenter
}
background: CustomRect {
opacity: control.enabled ? 1.0 : 0.5
radius: control.radius
color: control.bgColor
}
StateLayer {
radius: control.radius
function onClicked(): void {
control.clicked();
}
radius: control.radius
}
}
+14 -16
View File
@@ -5,35 +5,33 @@ import qs.Config
CheckBox {
id: control
property int checkWidth: 20
property int checkHeight: 20
property int checkWidth: 20
contentItem: CustomText {
anchors.left: parent.left
anchors.leftMargin: control.checkWidth + control.leftPadding + 8
anchors.verticalCenter: parent.verticalCenter
font.pointSize: control.font.pointSize
text: control.text
}
indicator: CustomRect {
implicitWidth: control.checkWidth
implicitHeight: control.checkHeight
// x: control.leftPadding
// y: parent.implicitHeight / 2 - implicitHeight / 2
border.color: control.checked ? DynamicColors.palette.m3primary : "transparent"
color: DynamicColors.palette.m3surfaceVariant
implicitHeight: control.checkHeight
implicitWidth: control.checkWidth
radius: 4
CustomRect {
implicitWidth: control.checkWidth - (x * 2)
color: DynamicColors.palette.m3primary
implicitHeight: control.checkHeight - (y * 2)
implicitWidth: control.checkWidth - (x * 2)
radius: 3
visible: control.checked
x: 4
y: 4
radius: 3
color: DynamicColors.palette.m3primary
visible: control.checked
}
}
contentItem: CustomText {
text: control.text
font.pointSize: control.font.pointSize
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: control.checkWidth + control.leftPadding + 8
}
}
+6 -5
View File
@@ -2,11 +2,12 @@ import Quickshell.Widgets
import QtQuick
ClippingRectangle {
id: root
id: root
color: "transparent"
color: "transparent"
Behavior on color {
CAnim {}
}
Behavior on color {
CAnim {
}
}
}
+7 -7
View File
@@ -1,13 +1,13 @@
import QtQuick
Flickable {
id: root
id: root
maximumFlickVelocity: 3000
maximumFlickVelocity: 3000
rebound: Transition {
Anim {
properties: "x,y"
}
}
rebound: Transition {
Anim {
properties: "x,y"
}
}
}
+2 -2
View File
@@ -4,7 +4,7 @@ import Quickshell.Widgets
import QtQuick
IconImage {
id: root
id: root
asynchronous: true
asynchronous: true
}
+7 -7
View File
@@ -1,13 +1,13 @@
import QtQuick
ListView {
id: root
id: root
maximumFlickVelocity: 3000
maximumFlickVelocity: 3000
rebound: Transition {
Anim {
properties: "x,y"
}
}
rebound: Transition {
Anim {
properties: "x,y"
}
}
}
+12 -12
View File
@@ -1,19 +1,19 @@
import QtQuick
MouseArea {
property int scrollAccumulatedY: 0
property int scrollAccumulatedY: 0
function onWheel(event: WheelEvent): void {
}
function onWheel(event: WheelEvent): void {
}
onWheel: event => {
if (Math.sign(event.angleDelta.y) !== Math.sign(scrollAccumulatedY))
scrollAccumulatedY = 0;
scrollAccumulatedY += event.angleDelta.y;
onWheel: event => {
if (Math.sign(event.angleDelta.y) !== Math.sign(scrollAccumulatedY))
scrollAccumulatedY = 0;
scrollAccumulatedY += event.angleDelta.y;
if (Math.abs(scrollAccumulatedY) >= 120) {
onWheel(event);
scrollAccumulatedY = 0;
}
}
if (Math.abs(scrollAccumulatedY) >= 120) {
onWheel(event);
scrollAccumulatedY = 0;
}
}
}
+40 -42
View File
@@ -3,53 +3,51 @@ import QtQuick.Templates
import qs.Config
RadioButton {
id: root
id: root
font.pointSize: 12
font.pointSize: 12
implicitHeight: Math.max(implicitIndicatorHeight, implicitContentHeight)
implicitWidth: implicitIndicatorWidth + implicitContentWidth + contentItem.anchors.leftMargin
implicitWidth: implicitIndicatorWidth + implicitContentWidth + contentItem.anchors.leftMargin
implicitHeight: Math.max(implicitIndicatorHeight, implicitContentHeight)
contentItem: CustomText {
anchors.left: outerCircle.right
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
font.pointSize: root.font.pointSize
text: root.text
}
indicator: Rectangle {
id: outerCircle
indicator: Rectangle {
id: outerCircle
anchors.verticalCenter: parent.verticalCenter
border.color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
border.width: 2
color: "transparent"
implicitHeight: 16
implicitWidth: 16
radius: 1000
implicitWidth: 16
implicitHeight: 16
radius: 1000
color: "transparent"
border.color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
border.width: 2
anchors.verticalCenter: parent.verticalCenter
Behavior on border.color {
CAnim {
}
}
StateLayer {
anchors.margins: -7
color: root.checked ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3primary
z: -1
StateLayer {
function onClicked(): void {
root.click();
}
function onClicked(): void {
root.click();
}
}
anchors.margins: -7
color: root.checked ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3primary
z: -1
}
CustomRect {
anchors.centerIn: parent
implicitWidth: 8
implicitHeight: 8
radius: 1000
color: Qt.alpha(DynamicColors.palette.m3primary, root.checked ? 1 : 0)
}
Behavior on border.color {
CAnim {}
}
}
contentItem: CustomText {
text: root.text
font.pointSize: root.font.pointSize
anchors.verticalCenter: parent.verticalCenter
anchors.left: outerCircle.right
anchors.leftMargin: 10
}
CustomRect {
anchors.centerIn: parent
color: Qt.alpha(DynamicColors.palette.m3primary, root.checked ? 1 : 0)
implicitHeight: 8
implicitWidth: 8
radius: 1000
}
}
}
+6 -5
View File
@@ -1,11 +1,12 @@
import QtQuick
Rectangle {
id: root
id: root
color: "transparent"
color: "transparent"
Behavior on color {
CAnim {}
}
Behavior on color {
CAnim {
}
}
}
+163 -162
View File
@@ -3,186 +3,187 @@ import QtQuick
import QtQuick.Templates
ScrollBar {
id: root
id: root
required property Flickable flickable
property bool shouldBeActive
property real nonAnimPosition
property bool animating
property bool _updatingFromFlickable: false
property bool _updatingFromUser: false
property bool animating
required property Flickable flickable
property real nonAnimPosition
property bool shouldBeActive
onHoveredChanged: {
if (hovered)
shouldBeActive = true;
else
shouldBeActive = flickable.moving;
}
implicitWidth: 8
property bool _updatingFromFlickable: false
property bool _updatingFromUser: false
contentItem: CustomRect {
anchors.left: parent.left
anchors.right: parent.right
color: DynamicColors.palette.m3secondary
opacity: {
if (root.size === 1)
return 0;
if (fullMouse.pressed)
return 1;
if (mouse.containsMouse)
return 0.8;
if (root.policy === ScrollBar.AlwaysOn || root.shouldBeActive)
return 0.6;
return 0;
}
radius: 1000
// Sync nonAnimPosition with Qt's automatic position binding
onPositionChanged: {
if (_updatingFromUser) {
_updatingFromUser = false;
return;
}
if (position === nonAnimPosition) {
animating = false;
return;
}
if (!animating && !_updatingFromFlickable && !fullMouse.pressed) {
nonAnimPosition = position;
}
}
Behavior on opacity {
Anim {
}
}
// Sync nonAnimPosition with flickable when not animating
Connections {
target: flickable
function onContentYChanged() {
if (!animating && !fullMouse.pressed) {
_updatingFromFlickable = true;
const contentHeight = flickable.contentHeight;
const height = flickable.height;
if (contentHeight > height) {
nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height)));
} else {
nonAnimPosition = 0;
}
_updatingFromFlickable = false;
}
}
}
MouseArea {
id: mouse
Component.onCompleted: {
if (flickable) {
const contentHeight = flickable.contentHeight;
const height = flickable.height;
if (contentHeight > height) {
nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height)));
}
}
}
implicitWidth: 8
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
Behavior on position {
enabled: !fullMouse.pressed
contentItem: CustomRect {
anchors.left: parent.left
anchors.right: parent.right
opacity: {
if (root.size === 1)
return 0;
if (fullMouse.pressed)
return 1;
if (mouse.containsMouse)
return 0.8;
if (root.policy === ScrollBar.AlwaysOn || root.shouldBeActive)
return 0.6;
return 0;
}
radius: 1000
color: DynamicColors.palette.m3secondary
Anim {
}
}
MouseArea {
id: mouse
Component.onCompleted: {
if (flickable) {
const contentHeight = flickable.contentHeight;
const height = flickable.height;
if (contentHeight > height) {
nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height)));
}
}
}
onHoveredChanged: {
if (hovered)
shouldBeActive = true;
else
shouldBeActive = flickable.moving;
}
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
acceptedButtons: Qt.NoButton
}
// Sync nonAnimPosition with Qt's automatic position binding
onPositionChanged: {
if (_updatingFromUser) {
_updatingFromUser = false;
return;
}
if (position === nonAnimPosition) {
animating = false;
return;
}
if (!animating && !_updatingFromFlickable && !fullMouse.pressed) {
nonAnimPosition = position;
}
}
Behavior on opacity {
Anim {}
}
}
// Sync nonAnimPosition with flickable when not animating
Connections {
function onContentYChanged() {
if (!animating && !fullMouse.pressed) {
_updatingFromFlickable = true;
const contentHeight = flickable.contentHeight;
const height = flickable.height;
if (contentHeight > height) {
nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height)));
} else {
nonAnimPosition = 0;
}
_updatingFromFlickable = false;
}
}
Connections {
target: root.flickable
target: flickable
}
function onMovingChanged(): void {
if (root.flickable.moving)
root.shouldBeActive = true;
else
hideDelay.restart();
}
}
Connections {
function onMovingChanged(): void {
if (root.flickable.moving)
root.shouldBeActive = true;
else
hideDelay.restart();
}
Timer {
id: hideDelay
target: root.flickable
}
interval: 600
onTriggered: root.shouldBeActive = root.flickable.moving || root.hovered
}
Timer {
id: hideDelay
CustomMouseArea {
id: fullMouse
interval: 600
anchors.fill: parent
preventStealing: true
onTriggered: root.shouldBeActive = root.flickable.moving || root.hovered
}
onPressed: event => {
root.animating = true;
root._updatingFromUser = true;
const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2));
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
}
}
CustomMouseArea {
id: fullMouse
onPositionChanged: event => {
root._updatingFromUser = true;
const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2));
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
}
}
function onWheel(event: WheelEvent): void {
root.animating = true;
root._updatingFromUser = true;
let newPos = root.nonAnimPosition;
if (event.angleDelta.y > 0)
newPos = Math.max(0, root.nonAnimPosition - 0.1);
else if (event.angleDelta.y < 0)
newPos = Math.min(1 - root.size, root.nonAnimPosition + 0.1);
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
}
}
function onWheel(event: WheelEvent): void {
root.animating = true;
root._updatingFromUser = true;
let newPos = root.nonAnimPosition;
if (event.angleDelta.y > 0)
newPos = Math.max(0, root.nonAnimPosition - 0.1);
else if (event.angleDelta.y < 0)
newPos = Math.min(1 - root.size, root.nonAnimPosition + 0.1);
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
}
}
}
anchors.fill: parent
preventStealing: true
Behavior on position {
enabled: !fullMouse.pressed
Anim {}
}
onPositionChanged: event => {
root._updatingFromUser = true;
const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2));
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
}
}
onPressed: event => {
root.animating = true;
root._updatingFromUser = true;
const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2));
root.nonAnimPosition = newPos;
// Update flickable position
// Map scrollbar position [0, 1-size] to contentY [0, maxContentY]
if (root.flickable) {
const contentHeight = root.flickable.contentHeight;
const height = root.flickable.height;
if (contentHeight > height) {
const maxContentY = contentHeight - height;
const maxPos = 1 - root.size;
const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0;
root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY));
}
}
}
}
}
+36 -43
View File
@@ -3,50 +3,43 @@ import QtQuick.Templates
import qs.Config
Slider {
id: root
id: root
background: Item {
CustomRect {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
background: Item {
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
bottomRightRadius: root.implicitHeight / 6
color: DynamicColors.palette.m3primary
implicitWidth: root.handle.x - root.implicitHeight / 2
radius: 1000
topRightRadius: root.implicitHeight / 6
}
implicitWidth: root.handle.x - root.implicitHeight / 2
CustomRect {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
bottomLeftRadius: root.implicitHeight / 6
color: DynamicColors.tPalette.m3surfaceContainer
implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 2
radius: 1000
topLeftRadius: root.implicitHeight / 6
}
}
handle: CustomRect {
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3primary
implicitHeight: 15
implicitWidth: 5
radius: 1000
x: root.visualPosition * root.availableWidth - implicitWidth / 2
color: DynamicColors.palette.m3primary
radius: 1000
topRightRadius: root.implicitHeight / 6
bottomRightRadius: root.implicitHeight / 6
}
CustomRect {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 2
color: DynamicColors.tPalette.m3surfaceContainer
radius: 1000
topLeftRadius: root.implicitHeight / 6
bottomLeftRadius: root.implicitHeight / 6
}
}
handle: CustomRect {
x: root.visualPosition * root.availableWidth - implicitWidth / 2
implicitWidth: 5
implicitHeight: 15
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3primary
radius: 1000
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: Qt.PointingHandCursor
}
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
}
}
+127 -122
View File
@@ -4,147 +4,152 @@ import QtQuick.Shapes
import qs.Config
Switch {
id: root
id: root
property int cLayer: 1
property int cLayer: 1
implicitWidth: implicitIndicatorWidth
implicitHeight: implicitIndicatorHeight
implicitHeight: implicitIndicatorHeight
implicitWidth: implicitIndicatorWidth
indicator: CustomRect {
radius: 1000
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
indicator: CustomRect {
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer)
implicitHeight: 13 + 7 * 2
implicitWidth: implicitHeight * 1.7
radius: 1000
implicitWidth: implicitHeight * 1.7
implicitHeight: 13 + 7 * 2
CustomRect {
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight
CustomRect {
readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight
anchors.verticalCenter: parent.verticalCenter
color: root.checked ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
implicitHeight: parent.implicitHeight - 10
implicitWidth: nonAnimWidth
radius: 1000
x: root.checked ? parent.implicitWidth - nonAnimWidth - 10 / 2 : 10 / 2
radius: 1000
color: root.checked ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1)
Behavior on implicitWidth {
Anim {
}
}
Behavior on x {
Anim {
}
}
x: root.checked ? parent.implicitWidth - nonAnimWidth - 10 / 2 : 10 / 2
implicitWidth: nonAnimWidth
implicitHeight: parent.implicitHeight - 10
anchors.verticalCenter: parent.verticalCenter
CustomRect {
anchors.fill: parent
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0
radius: parent.radius
CustomRect {
anchors.fill: parent
radius: parent.radius
Behavior on opacity {
Anim {
}
}
}
color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0
Shape {
id: icon
Behavior on opacity {
Anim {}
}
}
property point end1: {
if (root.pressed) {
if (root.checked)
return Qt.point(width * 0.4, height / 2);
return Qt.point(width * 0.8, height / 2);
}
if (root.checked)
return Qt.point(width * 0.4, height * 0.7);
return Qt.point(width * 0.85, height * 0.85);
}
property point end2: {
if (root.pressed)
return Qt.point(width, height / 2);
if (root.checked)
return Qt.point(width * 0.85, height * 0.2);
return Qt.point(width * 0.85, height * 0.15);
}
property point start1: {
if (root.pressed)
return Qt.point(width * 0.1, height / 2);
if (root.checked)
return Qt.point(width * 0.15, height / 2);
return Qt.point(width * 0.15, height * 0.15);
}
property point start2: {
if (root.pressed) {
if (root.checked)
return Qt.point(width * 0.4, height / 2);
return Qt.point(width * 0.2, height / 2);
}
if (root.checked)
return Qt.point(width * 0.4, height * 0.7);
return Qt.point(width * 0.15, height * 0.85);
}
Shape {
id: icon
anchors.centerIn: parent
asynchronous: true
height: parent.implicitHeight - Appearance.padding.small * 2
preferredRendererType: Shape.CurveRenderer
width: height
property point start1: {
if (root.pressed)
return Qt.point(width * 0.1, height / 2);
if (root.checked)
return Qt.point(width * 0.15, height / 2);
return Qt.point(width * 0.15, height * 0.15);
}
property point end1: {
if (root.pressed) {
if (root.checked)
return Qt.point(width * 0.4, height / 2);
return Qt.point(width * 0.8, height / 2);
}
if (root.checked)
return Qt.point(width * 0.4, height * 0.7);
return Qt.point(width * 0.85, height * 0.85);
}
property point start2: {
if (root.pressed) {
if (root.checked)
return Qt.point(width * 0.4, height / 2);
return Qt.point(width * 0.2, height / 2);
}
if (root.checked)
return Qt.point(width * 0.4, height * 0.7);
return Qt.point(width * 0.15, height * 0.85);
}
property point end2: {
if (root.pressed)
return Qt.point(width, height / 2);
if (root.checked)
return Qt.point(width * 0.85, height * 0.2);
return Qt.point(width * 0.85, height * 0.15);
}
Behavior on end1 {
PropAnim {
}
}
Behavior on end2 {
PropAnim {
}
}
Behavior on start1 {
PropAnim {
}
}
Behavior on start2 {
PropAnim {
}
}
anchors.centerIn: parent
width: height
height: parent.implicitHeight - Appearance.padding.small * 2
preferredRendererType: Shape.CurveRenderer
asynchronous: true
ShapePath {
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
fillColor: "transparent"
startX: icon.start1.x
startY: icon.start1.y
strokeColor: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest
strokeWidth: Appearance.font.size.larger * 0.15
ShapePath {
strokeWidth: Appearance.font.size.larger * 0.15
strokeColor: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest
fillColor: "transparent"
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
Behavior on strokeColor {
CAnim {
}
}
startX: icon.start1.x
startY: icon.start1.y
PathLine {
x: icon.end1.x
y: icon.end1.y
}
PathLine {
x: icon.end1.x
y: icon.end1.y
}
PathMove {
x: icon.start2.x
y: icon.start2.y
}
PathLine {
x: icon.end2.x
y: icon.end2.y
}
PathMove {
x: icon.start2.x
y: icon.start2.y
}
Behavior on strokeColor {
CAnim {}
}
}
PathLine {
x: icon.end2.x
y: icon.end2.y
}
}
}
}
}
Behavior on start1 {
PropAnim {}
}
Behavior on end1 {
PropAnim {}
}
Behavior on start2 {
PropAnim {}
}
Behavior on end2 {
PropAnim {}
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
enabled: false
}
Behavior on x {
Anim {}
}
Behavior on implicitWidth {
Anim {}
}
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
enabled: false
}
component PropAnim: PropertyAnimation {
component PropAnim: PropertyAnimation {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
easing.type: Easing.BezierSpline
}
easing.type: Easing.BezierSpline
}
}
+38 -35
View File
@@ -4,45 +4,48 @@ import QtQuick
import qs.Config
Text {
id: root
id: root
property bool animate: false
property string animateProp: "scale"
property real animateFrom: 0
property real animateTo: 1
property int animateDuration: 400
property bool animate: false
property int animateDuration: 400
property real animateFrom: 0
property string animateProp: "scale"
property real animateTo: 1
renderType: Text.NativeRendering
textFormat: Text.PlainText
color: DynamicColors.palette.m3onSurface
font.family: Appearance.font.family.sans
font.pointSize: 12
color: DynamicColors.palette.m3onSurface
font.family: Appearance.font.family.sans
font.pointSize: 12
renderType: Text.NativeRendering
textFormat: Text.PlainText
Behavior on color {
CAnim {}
}
Behavior on color {
CAnim {
}
}
Behavior on text {
enabled: root.animate
Behavior on text {
enabled: root.animate
SequentialAnimation {
Anim {
easing.bezierCurve: MaterialEasing.standardAccel
to: root.animateFrom
}
SequentialAnimation {
Anim {
to: root.animateFrom
easing.bezierCurve: MaterialEasing.standardAccel
}
PropertyAction {}
Anim {
to: root.animateTo
easing.bezierCurve: MaterialEasing.standardDecel
}
}
}
PropertyAction {
}
component Anim: NumberAnimation {
target: root
property: root.animateProp.split(",").length === 1 ? root.animateProp : ""
properties: root.animateProp.split(",").length > 1 ? root.animateProp : ""
duration: root.animateDuration / 2
easing.type: Easing.BezierSpline
}
Anim {
easing.bezierCurve: MaterialEasing.standardDecel
to: root.animateTo
}
}
}
component Anim: NumberAnimation {
duration: root.animateDuration / 2
easing.type: Easing.BezierSpline
properties: root.animateProp.split(",").length > 1 ? root.animateProp : ""
property: root.animateProp.split(",").length === 1 ? root.animateProp : ""
target: root
}
}
+54 -53
View File
@@ -5,70 +5,71 @@ import QtQuick.Controls
import qs.Config
TextField {
id: root
id: root
color: DynamicColors.palette.m3onSurface
placeholderTextColor: DynamicColors.palette.m3outline
font.family: Appearance.font.family.sans
font.pointSize: Appearance.font.size.smaller
renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
cursorVisible: !readOnly
background: null
color: DynamicColors.palette.m3onSurface
cursorVisible: !readOnly
font.family: Appearance.font.family.sans
font.pointSize: Appearance.font.size.smaller
placeholderTextColor: DynamicColors.palette.m3outline
renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
background: null
Behavior on color {
CAnim {
}
}
cursorDelegate: CustomRect {
id: cursor
cursorDelegate: CustomRect {
id: cursor
property bool disableBlink
property bool disableBlink
color: DynamicColors.palette.m3primary
implicitWidth: 2
radius: Appearance.rounding.normal
implicitWidth: 2
color: DynamicColors.palette.m3primary
radius: Appearance.rounding.normal
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.small
}
}
Connections {
target: root
Connections {
function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1;
cursor.disableBlink = true;
enableBlink.restart();
}
}
function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1;
cursor.disableBlink = true;
enableBlink.restart();
}
}
}
target: root
}
Timer {
id: enableBlink
Timer {
id: enableBlink
interval: 100
onTriggered: cursor.disableBlink = false
}
interval: 100
Timer {
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
repeat: true
triggeredOnStart: true
interval: 500
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
onTriggered: cursor.disableBlink = false
}
Binding {
when: !root.activeFocus || !root.cursorVisible
cursor.opacity: 0
}
Timer {
interval: 500
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
Behavior on opacity {
Anim {
duration: Appearance.anim.durations.small
}
}
}
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
Behavior on color {
CAnim {}
}
Behavior on placeholderTextColor {
CAnim {}
}
Binding {
cursor.opacity: 0
when: !root.activeFocus || !root.cursorVisible
}
}
Behavior on placeholderTextColor {
CAnim {
}
}
}
+17 -15
View File
@@ -3,21 +3,23 @@ import QtQuick.Controls
import qs.Components
ToolTip {
id: root
property bool extraVisibleCondition: true
property bool alternativeVisibleCondition: false
readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition
verticalPadding: 5
horizontalPadding: 10
background: null
id: root
visible: internalVisibleCondition
property bool alternativeVisibleCondition: false
property bool extraVisibleCondition: true
readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition
contentItem: CustomTooltipContent {
id: contentItem
text: root.text
shown: root.internalVisibleCondition
horizontalPadding: root.horizontalPadding
verticalPadding: root.verticalPadding
}
background: null
horizontalPadding: 10
verticalPadding: 5
visible: internalVisibleCondition
contentItem: CustomTooltipContent {
id: contentItem
horizontalPadding: root.horizontalPadding
shown: root.internalVisibleCondition
text: root.text
verticalPadding: root.verticalPadding
}
}
+44 -37
View File
@@ -3,45 +3,52 @@ import qs.Components
import qs.Config
Item {
id: root
required property string text
property bool shown: false
property real horizontalPadding: 10
property real verticalPadding: 5
implicitWidth: tooltipTextObject.implicitWidth + 2 * root.horizontalPadding
implicitHeight: tooltipTextObject.implicitHeight + 2 * root.verticalPadding
id: root
property bool isVisible: backgroundRectangle.implicitHeight > 0
property real horizontalPadding: 10
property bool isVisible: backgroundRectangle.implicitHeight > 0
property bool shown: false
required property string text
property real verticalPadding: 5
Rectangle {
id: backgroundRectangle
anchors {
bottom: root.bottom
horizontalCenter: root.horizontalCenter
}
color: DynamicColors.tPalette.m3inverseSurface ?? "#3C4043"
radius: 8
opacity: shown ? 1 : 0
implicitWidth: shown ? (tooltipTextObject.implicitWidth + 2 * root.horizontalPadding) : 0
implicitHeight: shown ? (tooltipTextObject.implicitHeight + 2 * root.verticalPadding) : 0
clip: true
implicitHeight: tooltipTextObject.implicitHeight + 2 * root.verticalPadding
implicitWidth: tooltipTextObject.implicitWidth + 2 * root.horizontalPadding
Behavior on implicitWidth {
Anim {}
}
Behavior on implicitHeight {
Anim {}
}
Behavior on opacity {
Anim {}
}
Rectangle {
id: backgroundRectangle
CustomText {
id: tooltipTextObject
anchors.centerIn: parent
text: root.text
color: DynamicColors.palette.m3inverseOnSurface ?? "#FFFFFF"
wrapMode: Text.Wrap
}
}
clip: true
color: DynamicColors.tPalette.m3inverseSurface ?? "#3C4043"
implicitHeight: shown ? (tooltipTextObject.implicitHeight + 2 * root.verticalPadding) : 0
implicitWidth: shown ? (tooltipTextObject.implicitWidth + 2 * root.horizontalPadding) : 0
opacity: shown ? 1 : 0
radius: 8
Behavior on implicitHeight {
Anim {
}
}
Behavior on implicitWidth {
Anim {
}
}
Behavior on opacity {
Anim {
}
}
anchors {
bottom: root.bottom
horizontalCenter: root.horizontalCenter
}
CustomText {
id: tooltipTextObject
anchors.centerIn: parent
color: DynamicColors.palette.m3inverseOnSurface ?? "#FFFFFF"
text: root.text
wrapMode: Text.Wrap
}
}
}
+10 -9
View File
@@ -3,15 +3,16 @@ import QtQuick
import QtQuick.Effects
RectangularShadow {
property int level
property real dp: [0, 1, 3, 6, 8, 12][level]
property real dp: [0, 1, 3, 6, 8, 12][level]
property int level
color: Qt.alpha(DynamicColors.palette.m3shadow, 0.7)
blur: (dp * 5) ** 0.7
spread: -dp * 0.3 + (dp * 0.1) ** 2
offset.y: dp / 2
blur: (dp * 5) ** 0.7
color: Qt.alpha(DynamicColors.palette.m3shadow, 0.7)
offset.y: dp / 2
spread: -dp * 0.3 + (dp * 0.1) ** 2
Behavior on dp {
Anim {}
}
Behavior on dp {
Anim {
}
}
}
+34 -38
View File
@@ -2,47 +2,43 @@ import qs.Config
import QtQuick
CustomRect {
required property int extra
required property int extra
anchors.right: parent.right
anchors.margins: 8
anchors.margins: 8
anchors.right: parent.right
color: DynamicColors.palette.m3tertiary
implicitHeight: count.implicitHeight + 4 * 2
implicitWidth: count.implicitWidth + 8 * 2
opacity: extra > 0 ? 1 : 0
radius: 8
scale: extra > 0 ? 1 : 0.5
color: DynamicColors.palette.m3tertiary
radius: 8
implicitWidth: count.implicitWidth + 8 * 2
implicitHeight: count.implicitHeight + 4 * 2
opacity: extra > 0 ? 1 : 0
scale: extra > 0 ? 1 : 0.5
Elevation {
anchors.fill: parent
radius: parent.radius
opacity: parent.opacity
z: -1
level: 2
}
CustomText {
id: count
anchors.centerIn: parent
animate: parent.opacity > 0
text: qsTr("+%1").arg(parent.extra)
color: DynamicColors.palette.m3onTertiary
}
Behavior on opacity {
Anim {
Behavior on opacity {
Anim {
duration: MaterialEasing.expressiveEffectsTime
}
}
Behavior on scale {
Anim {
}
}
Behavior on scale {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
Elevation {
anchors.fill: parent
level: 2
opacity: parent.opacity
radius: parent.radius
z: -1
}
CustomText {
id: count
anchors.centerIn: parent
animate: parent.opacity > 0
color: DynamicColors.palette.m3onTertiary
text: qsTr("+%1").arg(parent.extra)
}
}
+108 -111
View File
@@ -3,142 +3,139 @@ import QtQuick.Templates
import qs.Config
Slider {
id: root
id: root
required property string icon
property real oldValue
property bool initialized
property color color: DynamicColors.palette.m3secondary
required property string icon
property bool initialized
property real oldValue
orientation: Qt.Vertical
orientation: Qt.Vertical
background: CustomRect {
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
radius: Appearance.rounding.full
background: CustomRect {
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
radius: Appearance.rounding.full
CustomRect {
anchors.left: parent.left
anchors.right: parent.right
CustomRect {
anchors.left: parent.left
anchors.right: parent.right
color: root.color
implicitHeight: parent.height - y
radius: parent.radius
y: root.handle.y
}
}
handle: Item {
id: handle
y: root.handle.y
implicitHeight: parent.height - y
property alias moving: icon.moving
color: root.color
radius: parent.radius
}
}
implicitHeight: root.width
implicitWidth: root.width
y: root.visualPosition * (root.availableHeight - height)
handle: Item {
id: handle
Elevation {
anchors.fill: parent
level: handleInteraction.containsMouse ? 2 : 1
radius: rect.radius
}
property alias moving: icon.moving
CustomRect {
id: rect
y: root.visualPosition * (root.availableHeight - height)
implicitWidth: root.width
implicitHeight: root.width
anchors.fill: parent
color: DynamicColors.palette.m3inverseSurface
radius: Appearance.rounding.full
Elevation {
anchors.fill: parent
radius: rect.radius
level: handleInteraction.containsMouse ? 2 : 1
}
MouseArea {
id: handleInteraction
CustomRect {
id: rect
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
anchors.fill: parent
MaterialIcon {
id: icon
color: DynamicColors.palette.m3inverseSurface
radius: Appearance.rounding.full
property bool moving
MouseArea {
id: handleInteraction
function update(): void {
animate = !moving;
binding.when = moving;
font.pointSize = moving ? Appearance.font.size.small : Appearance.font.size.larger;
font.family = moving ? Appearance.font.family.sans : Appearance.font.family.material;
}
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.NoButton
}
anchors.centerIn: parent
color: DynamicColors.palette.m3inverseOnSurface
text: root.icon
MaterialIcon {
id: icon
onMovingChanged: anim.restart()
property bool moving
Binding {
id: binding
function update(): void {
animate = !moving;
binding.when = moving;
font.pointSize = moving ? Appearance.font.size.small : Appearance.font.size.larger;
font.family = moving ? Appearance.font.family.sans : Appearance.font.family.material;
}
property: "text"
target: icon
value: Math.round(root.value * 100)
when: false
}
text: root.icon
color: DynamicColors.palette.m3inverseOnSurface
anchors.centerIn: parent
SequentialAnimation {
id: anim
onMovingChanged: anim.restart()
Anim {
duration: Appearance.anim.durations.normal / 2
easing.bezierCurve: Appearance.anim.curves.standardAccel
property: "scale"
target: icon
to: 0
}
Binding {
id: binding
ScriptAction {
script: icon.update()
}
target: icon
property: "text"
value: Math.round(root.value * 100)
when: false
}
Anim {
duration: Appearance.anim.durations.normal / 2
easing.bezierCurve: Appearance.anim.curves.standardDecel
property: "scale"
target: icon
to: 1
}
}
}
}
}
Behavior on value {
Anim {
duration: Appearance.anim.durations.large
}
}
SequentialAnimation {
id: anim
onPressedChanged: handle.moving = pressed
onValueChanged: {
if (!initialized) {
initialized = true;
return;
}
if (Math.abs(value - oldValue) < 0.01)
return;
oldValue = value;
handle.moving = true;
stateChangeDelay.restart();
}
Anim {
target: icon
property: "scale"
to: 0
duration: Appearance.anim.durations.normal / 2
easing.bezierCurve: Appearance.anim.curves.standardAccel
}
ScriptAction {
script: icon.update()
}
Anim {
target: icon
property: "scale"
to: 1
duration: Appearance.anim.durations.normal / 2
easing.bezierCurve: Appearance.anim.curves.standardDecel
}
}
}
}
}
Timer {
id: stateChangeDelay
onPressedChanged: handle.moving = pressed
interval: 500
onValueChanged: {
if (!initialized) {
initialized = true;
return;
}
if (Math.abs(value - oldValue) < 0.01)
return;
oldValue = value;
handle.moving = true;
stateChangeDelay.restart();
}
Timer {
id: stateChangeDelay
interval: 500
onTriggered: {
if (!root.pressed)
handle.moving = false;
}
}
Behavior on value {
Anim {
duration: Appearance.anim.durations.large
}
}
onTriggered: {
if (!root.pressed)
handle.moving = false;
}
}
}
+62 -63
View File
@@ -2,80 +2,79 @@ import qs.Config
import QtQuick
CustomRect {
id: root
id: root
enum Type {
Filled,
Tonal,
Text
}
enum Type {
Filled,
Tonal,
Text
}
property alias icon: label.text
property bool checked
property bool toggle
property real padding: type === IconButton.Text ? 10 / 2 : 7
property alias font: label.font
property int type: IconButton.Filled
property bool disabled
property color activeColour: type === IconButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
property color activeOnColour: type === IconButton.Filled ? DynamicColors.palette.m3onPrimary : type === IconButton.Tonal ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3primary
property bool checked
property bool disabled
property color disabledColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledOnColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
property alias font: label.font
property alias icon: label.text
property color inactiveColour: {
if (!toggle && type === IconButton.Filled)
return DynamicColors.palette.m3primary;
return type === IconButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
}
property color inactiveOnColour: {
if (!toggle && type === IconButton.Filled)
return DynamicColors.palette.m3onPrimary;
return type === IconButton.Tonal ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant;
}
property bool internalChecked
property alias label: label
property real padding: type === IconButton.Text ? 10 / 2 : 7
property alias radiusAnim: radiusAnim
property alias stateLayer: stateLayer
property bool toggle
property int type: IconButton.Filled
property alias stateLayer: stateLayer
property alias label: label
property alias radiusAnim: radiusAnim
signal clicked
property bool internalChecked
property color activeColour: type === IconButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
property color inactiveColour: {
if (!toggle && type === IconButton.Filled)
return DynamicColors.palette.m3primary;
return type === IconButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
}
property color activeOnColour: type === IconButton.Filled ? DynamicColors.palette.m3onPrimary : type === IconButton.Tonal ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3primary
property color inactiveOnColour: {
if (!toggle && type === IconButton.Filled)
return DynamicColors.palette.m3onPrimary;
return type === IconButton.Tonal ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant;
}
property color disabledColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
property color disabledOnColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour
implicitHeight: label.implicitHeight + padding * 2
implicitWidth: implicitHeight
radius: internalChecked ? 6 : implicitHeight / 2 * Math.min(1, 1)
signal clicked
Behavior on radius {
Anim {
id: radiusAnim
onCheckedChanged: internalChecked = checked
}
}
radius: internalChecked ? 6 : implicitHeight / 2 * Math.min(1, 1)
color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour
onCheckedChanged: internalChecked = checked
implicitWidth: implicitHeight
implicitHeight: label.implicitHeight + padding * 2
StateLayer {
id: stateLayer
StateLayer {
id: stateLayer
function onClicked(): void {
if (root.toggle)
root.internalChecked = !root.internalChecked;
root.clicked();
}
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
disabled: root.disabled
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
disabled: root.disabled
}
function onClicked(): void {
if (root.toggle)
root.internalChecked = !root.internalChecked;
root.clicked();
}
}
MaterialIcon {
id: label
MaterialIcon {
id: label
anchors.centerIn: parent
color: root.disabled ? root.disabledOnColour : root.internalChecked ? root.activeOnColour : root.inactiveOnColour
fill: !root.toggle || root.internalChecked ? 1 : 0
anchors.centerIn: parent
color: root.disabled ? root.disabledOnColour : root.internalChecked ? root.activeOnColour : root.inactiveOnColour
fill: !root.toggle || root.internalChecked ? 1 : 0
Behavior on fill {
Anim {}
}
}
Behavior on radius {
Anim {
id: radiusAnim
}
}
Behavior on fill {
Anim {
}
}
}
}
+10 -10
View File
@@ -1,15 +1,15 @@
import qs.Config
CustomText {
property real fill
property int grade: DynamicColors.light ? 0 : -25
property real fill
property int grade: DynamicColors.light ? 0 : -25
font.family: "Material Symbols Rounded"
font.pointSize: 15
font.variableAxes: ({
FILL: fill.toFixed(1),
GRAD: grade,
opsz: fontInfo.pixelSize,
wght: fontInfo.weight
})
font.family: "Material Symbols Rounded"
font.pointSize: 15
font.variableAxes: ({
FILL: fill.toFixed(1),
GRAD: grade,
opsz: fontInfo.pixelSize,
wght: fontInfo.weight
})
}
+3 -3
View File
@@ -2,8 +2,8 @@ import Quickshell
import QtQuick
ShaderEffect {
required property Item source
required property Item maskSource
required property Item maskSource
required property Item source
fragmentShader: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/shaders/opacitymask.frag.qsb`)
fragmentShader: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/shaders/opacitymask.frag.qsb`)
}
+3 -3
View File
@@ -1,8 +1,8 @@
import QtQuick
QtObject {
required property var service
required property var service
Component.onCompleted: service.refCount++
Component.onDestruction: service.refCount--
Component.onCompleted: service.refCount++
Component.onDestruction: service.refCount--
}
+72 -71
View File
@@ -2,94 +2,95 @@ import qs.Config
import QtQuick
MouseArea {
id: root
id: root
property bool disabled
property color color: DynamicColors.palette.m3onSurface
property real radius: parent?.radius ?? 0
property alias rect: hoverLayer
property color color: DynamicColors.palette.m3onSurface
property bool disabled
property real radius: parent?.radius ?? 0
property alias rect: hoverLayer
function onClicked(): void {
}
function onClicked(): void {
}
anchors.fill: parent
anchors.fill: parent
cursorShape: disabled ? undefined : Qt.PointingHandCursor
enabled: !disabled
hoverEnabled: true
enabled: !disabled
cursorShape: disabled ? undefined : Qt.PointingHandCursor
hoverEnabled: true
onClicked: event => !disabled && onClicked(event)
onPressed: event => {
if (disabled)
return;
onPressed: event => {
if (disabled)
return;
rippleAnim.x = event.x;
rippleAnim.y = event.y;
rippleAnim.x = event.x;
rippleAnim.y = event.y;
const dist = (ox, oy) => ox * ox + oy * oy;
rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y), dist(event.x, height - event.y), dist(width - event.x, event.y), dist(width - event.x, height - event.y)));
const dist = (ox, oy) => ox * ox + oy * oy;
rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y), dist(event.x, height - event.y), dist(width - event.x, event.y), dist(width - event.x, height - event.y)));
rippleAnim.restart();
}
rippleAnim.restart();
}
SequentialAnimation {
id: rippleAnim
onClicked: event => !disabled && onClicked(event)
property real radius
property real x
property real y
SequentialAnimation {
id: rippleAnim
PropertyAction {
property: "x"
target: ripple
value: rippleAnim.x
}
property real x
property real y
property real radius
PropertyAction {
property: "y"
target: ripple
value: rippleAnim.y
}
PropertyAction {
target: ripple
property: "x"
value: rippleAnim.x
}
PropertyAction {
target: ripple
property: "y"
value: rippleAnim.y
}
PropertyAction {
target: ripple
property: "opacity"
value: 0.08
}
Anim {
target: ripple
properties: "implicitWidth,implicitHeight"
from: 0
to: rippleAnim.radius * 2
easing.bezierCurve: MaterialEasing.standardDecel
}
Anim {
target: ripple
property: "opacity"
to: 0
}
}
PropertyAction {
property: "opacity"
target: ripple
value: 0.08
}
CustomClippingRect {
id: hoverLayer
Anim {
easing.bezierCurve: MaterialEasing.standardDecel
from: 0
properties: "implicitWidth,implicitHeight"
target: ripple
to: rippleAnim.radius * 2
}
anchors.fill: parent
Anim {
property: "opacity"
target: ripple
to: 0
}
}
CustomClippingRect {
id: hoverLayer
anchors.fill: parent
border.pixelAligned: false
color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0)
radius: root.radius
color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0)
radius: root.radius
CustomRect {
id: ripple
CustomRect {
id: ripple
radius: 1000
color: root.color
opacity: 0
border.pixelAligned: false
color: root.color
opacity: 0
radius: 1000
transform: Translate {
x: -ripple.width / 2
y: -ripple.height / 2
}
}
}
transform: Translate {
x: -ripple.width / 2
y: -ripple.height / 2
}
}
}
}
+110 -112
View File
@@ -5,129 +5,127 @@ import qs.Components
import qs.Config
CustomRect {
id: root
id: root
required property Toast modelData
required property Toast modelData
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: layout.implicitHeight + Appearance.padding.smaller * 2
anchors.left: parent.left
anchors.right: parent.right
border.color: {
let colour = DynamicColors.palette.m3outlineVariant;
if (root.modelData.type === Toast.Success)
colour = DynamicColors.palette.m3success;
if (root.modelData.type === Toast.Warning)
colour = DynamicColors.palette.m3secondaryContainer;
if (root.modelData.type === Toast.Error)
colour = DynamicColors.palette.m3error;
return Qt.alpha(colour, 0.3);
}
border.width: 1
color: {
if (root.modelData.type === Toast.Success)
return DynamicColors.palette.m3successContainer;
if (root.modelData.type === Toast.Warning)
return DynamicColors.palette.m3secondary;
if (root.modelData.type === Toast.Error)
return DynamicColors.palette.m3errorContainer;
return DynamicColors.palette.m3surface;
}
implicitHeight: layout.implicitHeight + Appearance.padding.smaller * 2
radius: Appearance.rounding.normal
radius: Appearance.rounding.normal
color: {
if (root.modelData.type === Toast.Success)
return DynamicColors.palette.m3successContainer;
if (root.modelData.type === Toast.Warning)
return DynamicColors.palette.m3secondary;
if (root.modelData.type === Toast.Error)
return DynamicColors.palette.m3errorContainer;
return DynamicColors.palette.m3surface;
}
Behavior on border.color {
CAnim {
}
}
border.width: 1
border.color: {
let colour = DynamicColors.palette.m3outlineVariant;
if (root.modelData.type === Toast.Success)
colour = DynamicColors.palette.m3success;
if (root.modelData.type === Toast.Warning)
colour = DynamicColors.palette.m3secondaryContainer;
if (root.modelData.type === Toast.Error)
colour = DynamicColors.palette.m3error;
return Qt.alpha(colour, 0.3);
}
Elevation {
anchors.fill: parent
level: 3
opacity: parent.opacity
radius: parent.radius
z: -1
}
Elevation {
anchors.fill: parent
radius: parent.radius
opacity: parent.opacity
z: -1
level: 3
}
RowLayout {
id: layout
RowLayout {
id: layout
anchors.fill: parent
anchors.leftMargin: Appearance.padding.normal
anchors.margins: Appearance.padding.smaller
anchors.rightMargin: Appearance.padding.normal
spacing: Appearance.spacing.normal
anchors.fill: parent
anchors.margins: Appearance.padding.smaller
anchors.leftMargin: Appearance.padding.normal
anchors.rightMargin: Appearance.padding.normal
spacing: Appearance.spacing.normal
CustomRect {
color: {
if (root.modelData.type === Toast.Success)
return DynamicColors.palette.m3success;
if (root.modelData.type === Toast.Warning)
return DynamicColors.palette.m3secondaryContainer;
if (root.modelData.type === Toast.Error)
return DynamicColors.palette.m3error;
return DynamicColors.palette.m3surfaceContainerHigh;
}
implicitHeight: icon.implicitHeight + Appearance.padding.smaller * 2
implicitWidth: implicitHeight
radius: Appearance.rounding.normal
CustomRect {
radius: Appearance.rounding.normal
color: {
if (root.modelData.type === Toast.Success)
return DynamicColors.palette.m3success;
if (root.modelData.type === Toast.Warning)
return DynamicColors.palette.m3secondaryContainer;
if (root.modelData.type === Toast.Error)
return DynamicColors.palette.m3error;
return DynamicColors.palette.m3surfaceContainerHigh;
}
MaterialIcon {
id: icon
implicitWidth: implicitHeight
implicitHeight: icon.implicitHeight + Appearance.padding.smaller * 2
anchors.centerIn: parent
color: {
if (root.modelData.type === Toast.Success)
return DynamicColors.palette.m3onSuccess;
if (root.modelData.type === Toast.Warning)
return DynamicColors.palette.m3onSecondaryContainer;
if (root.modelData.type === Toast.Error)
return DynamicColors.palette.m3onError;
return DynamicColors.palette.m3onSurfaceVariant;
}
font.pointSize: Math.round(Appearance.font.size.large * 1.2)
text: root.modelData.icon
}
}
MaterialIcon {
id: icon
ColumnLayout {
Layout.fillWidth: true
spacing: 0
anchors.centerIn: parent
text: root.modelData.icon
color: {
if (root.modelData.type === Toast.Success)
return DynamicColors.palette.m3onSuccess;
if (root.modelData.type === Toast.Warning)
return DynamicColors.palette.m3onSecondaryContainer;
if (root.modelData.type === Toast.Error)
return DynamicColors.palette.m3onError;
return DynamicColors.palette.m3onSurfaceVariant;
}
font.pointSize: Math.round(Appearance.font.size.large * 1.2)
}
}
CustomText {
id: title
ColumnLayout {
Layout.fillWidth: true
spacing: 0
Layout.fillWidth: true
color: {
if (root.modelData.type === Toast.Success)
return DynamicColors.palette.m3onSuccessContainer;
if (root.modelData.type === Toast.Warning)
return DynamicColors.palette.m3onSecondary;
if (root.modelData.type === Toast.Error)
return DynamicColors.palette.m3onErrorContainer;
return DynamicColors.palette.m3onSurface;
}
elide: Text.ElideRight
font.pointSize: Appearance.font.size.normal
text: root.modelData.title
}
CustomText {
id: title
Layout.fillWidth: true
text: root.modelData.title
color: {
if (root.modelData.type === Toast.Success)
return DynamicColors.palette.m3onSuccessContainer;
if (root.modelData.type === Toast.Warning)
return DynamicColors.palette.m3onSecondary;
if (root.modelData.type === Toast.Error)
return DynamicColors.palette.m3onErrorContainer;
return DynamicColors.palette.m3onSurface;
}
font.pointSize: Appearance.font.size.normal
elide: Text.ElideRight
}
CustomText {
Layout.fillWidth: true
textFormat: Text.StyledText
text: root.modelData.message
color: {
if (root.modelData.type === Toast.Success)
return DynamicColors.palette.m3onSuccessContainer;
if (root.modelData.type === Toast.Warning)
return DynamicColors.palette.m3onSecondary;
if (root.modelData.type === Toast.Error)
return DynamicColors.palette.m3onErrorContainer;
return DynamicColors.palette.m3onSurface;
}
opacity: 0.8
elide: Text.ElideRight
}
}
}
Behavior on border.color {
CAnim {}
}
CustomText {
Layout.fillWidth: true
color: {
if (root.modelData.type === Toast.Success)
return DynamicColors.palette.m3onSuccessContainer;
if (root.modelData.type === Toast.Warning)
return DynamicColors.palette.m3onSecondary;
if (root.modelData.type === Toast.Error)
return DynamicColors.palette.m3onErrorContainer;
return DynamicColors.palette.m3onSurface;
}
elide: Text.ElideRight
opacity: 0.8
text: root.modelData.message
textFormat: Text.StyledText
}
}
}
}
+112 -113
View File
@@ -7,137 +7,136 @@ import qs.Components
import qs.Config
Item {
id: root
id: root
readonly property int spacing: Appearance.spacing.small
property bool flag
property bool flag
readonly property int spacing: Appearance.spacing.small
implicitWidth: Config.utilities.sizes.toastWidth - Appearance.padding.normal * 2
implicitHeight: {
let h = -spacing;
for (let i = 0; i < repeater.count; i++) {
const item = repeater.itemAt(i) as ToastWrapper;
if (!item.modelData.closed && !item.previewHidden)
h += item.implicitHeight + spacing;
}
return h;
}
implicitHeight: {
let h = -spacing;
for (let i = 0; i < repeater.count; i++) {
const item = repeater.itemAt(i) as ToastWrapper;
if (!item.modelData.closed && !item.previewHidden)
h += item.implicitHeight + spacing;
}
return h;
}
implicitWidth: Config.utilities.sizes.toastWidth - Appearance.padding.normal * 2
Repeater {
id: repeater
Repeater {
id: repeater
model: ScriptModel {
values: {
const toasts = [];
let count = 0;
for (const toast of Toaster.toasts) {
toasts.push(toast);
if (!toast.closed) {
count++;
if (count > Config.utilities.maxToasts)
break;
}
}
return toasts;
}
onValuesChanged: root.flagChanged()
}
model: ScriptModel {
values: {
const toasts = [];
let count = 0;
for (const toast of Toaster.toasts) {
toasts.push(toast);
if (!toast.closed) {
count++;
if (count > Config.utilities.maxToasts)
break;
}
}
return toasts;
}
ToastWrapper {}
}
onValuesChanged: root.flagChanged()
}
component ToastWrapper: MouseArea {
id: toast
ToastWrapper {
}
}
required property int index
required property Toast modelData
component ToastWrapper: MouseArea {
id: toast
readonly property bool previewHidden: {
let extraHidden = 0;
for (let i = 0; i < index; i++)
if (Toaster.toasts[i].closed)
extraHidden++;
return index >= Config.utilities.maxToasts + extraHidden;
}
required property int index
required property Toast modelData
readonly property bool previewHidden: {
let extraHidden = 0;
for (let i = 0; i < index; i++)
if (Toaster.toasts[i].closed)
extraHidden++;
return index >= Config.utilities.maxToasts + extraHidden;
}
onPreviewHiddenChanged: {
if (initAnim.running && previewHidden)
initAnim.stop();
}
acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
anchors.bottom: parent.bottom
anchors.bottomMargin: {
root.flag; // Force update
let y = 0;
for (let i = 0; i < index; i++) {
const item = repeater.itemAt(i) as ToastWrapper;
if (item && !item.modelData.closed && !item.previewHidden)
y += item.implicitHeight + root.spacing;
}
return y;
}
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: toastInner.implicitHeight
opacity: modelData.closed || previewHidden ? 0 : 1
scale: modelData.closed || previewHidden ? 0.7 : 1
opacity: modelData.closed || previewHidden ? 0 : 1
scale: modelData.closed || previewHidden ? 0.7 : 1
Behavior on anchors.bottomMargin {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
anchors.bottomMargin: {
root.flag; // Force update
let y = 0;
for (let i = 0; i < index; i++) {
const item = repeater.itemAt(i) as ToastWrapper;
if (item && !item.modelData.closed && !item.previewHidden)
y += item.implicitHeight + root.spacing;
}
return y;
}
Component.onCompleted: modelData.lock(this)
onClicked: modelData.close()
onPreviewHiddenChanged: {
if (initAnim.running && previewHidden)
initAnim.stop();
}
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
implicitHeight: toastInner.implicitHeight
Anim {
id: initAnim
acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
onClicked: modelData.close()
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
from: 0
properties: "opacity,scale"
target: toast
to: 1
Component.onCompleted: modelData.lock(this)
Component.onCompleted: running = !toast.previewHidden
}
Anim {
id: initAnim
ParallelAnimation {
running: toast.modelData.closed
Component.onCompleted: running = !toast.previewHidden
onFinished: toast.modelData.unlock(toast)
onStarted: toast.anchors.bottomMargin = toast.anchors.bottomMargin
target: toast
properties: "opacity,scale"
from: 0
to: 1
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
Anim {
property: "opacity"
target: toast
to: 0
}
ParallelAnimation {
running: toast.modelData.closed
onStarted: toast.anchors.bottomMargin = toast.anchors.bottomMargin
onFinished: toast.modelData.unlock(toast)
Anim {
property: "scale"
target: toast
to: 0.7
}
}
Anim {
target: toast
property: "opacity"
to: 0
}
Anim {
target: toast
property: "scale"
to: 0.7
}
}
ToastItem {
id: toastInner
ToastItem {
id: toastInner
modelData: toast.modelData
}
Behavior on opacity {
Anim {}
}
Behavior on scale {
Anim {}
}
Behavior on anchors.bottomMargin {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
}
modelData: toast.modelData
}
}
}
+8 -7
View File
@@ -1,12 +1,13 @@
import Quickshell.Io
JsonObject {
property Accents accents: Accents {}
property Accents accents: Accents {
}
component Accents: JsonObject {
property string primary: "#4080ff"
property string primaryAlt: "#60a0ff"
property string warning: "#ff6b6b"
property string warningAlt: "#ff8787"
}
component Accents: JsonObject {
property string primary: "#4080ff"
property string primaryAlt: "#60a0ff"
property string warning: "#ff6b6b"
property string warningAlt: "#ff8787"
}
}
+8 -8
View File
@@ -3,12 +3,12 @@ pragma Singleton
import Quickshell
Singleton {
// Literally just here to shorten accessing stuff :woe:
// Also kinda so I can keep accessing it with `Appearance.xxx` instead of `Conf.appearance.xxx`
readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding
readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing
readonly property AppearanceConf.Padding padding: Config.appearance.padding
readonly property AppearanceConf.FontStuff font: Config.appearance.font
readonly property AppearanceConf.Anim anim: Config.appearance.anim
readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency
readonly property AppearanceConf.Anim anim: Config.appearance.anim
readonly property AppearanceConf.FontStuff font: Config.appearance.font
readonly property AppearanceConf.Padding padding: Config.appearance.padding
// Literally just here to shorten accessing stuff :woe:
// Also kinda so I can keep accessing it with `Appearance.xxx` instead of `Conf.appearance.xxx`
readonly property AppearanceConf.Rounding rounding: Config.appearance.rounding
readonly property AppearanceConf.Spacing spacing: Config.appearance.spacing
readonly property AppearanceConf.Transparency transparency: Config.appearance.transparency
}
+90 -89
View File
@@ -1,94 +1,95 @@
import Quickshell.Io
JsonObject {
property Rounding rounding: Rounding {}
property Spacing spacing: Spacing {}
property Padding padding: Padding {}
property FontStuff font: FontStuff {}
property Anim anim: Anim {}
property Transparency transparency: Transparency {}
property Anim anim: Anim {
}
property FontStuff font: FontStuff {
}
property Padding padding: Padding {
}
property Rounding rounding: Rounding {
}
property Spacing spacing: Spacing {
}
property Transparency transparency: Transparency {
}
component Rounding: JsonObject {
property real scale: 1
property int small: 12 * scale
property int normal: 17 * scale
property int large: 25 * scale
property int full: 1000 * scale
}
component Spacing: JsonObject {
property real scale: 1
property int small: 7 * scale
property int smaller: 10 * scale
property int normal: 12 * scale
property int larger: 15 * scale
property int large: 20 * scale
}
component Padding: JsonObject {
property real scale: 1
property int small: 5 * scale
property int smaller: 7 * scale
property int normal: 10 * scale
property int larger: 12 * scale
property int large: 15 * scale
}
component FontFamily: JsonObject {
property string sans: "Segoe UI Variable Text"
property string mono: "CaskaydiaCove NF"
property string material: "Material Symbols Rounded"
property string clock: "Rubik"
}
component FontSize: JsonObject {
property real scale: 1
property int small: 11 * scale
property int smaller: 12 * scale
property int normal: 13 * scale
property int larger: 15 * scale
property int large: 18 * scale
property int extraLarge: 28 * scale
}
component FontStuff: JsonObject {
property FontFamily family: FontFamily {}
property FontSize size: FontSize {}
}
component AnimCurves: JsonObject {
property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
property list<real> standard: [0.2, 0, 0, 1, 1, 1]
property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1]
property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
}
component AnimDurations: JsonObject {
property real scale: 1
property int small: 200 * scale
property int normal: 400 * scale
property int large: 600 * scale
property int extraLarge: 1000 * scale
property int expressiveFastSpatial: 350 * scale
property int expressiveDefaultSpatial: 500 * scale
property int expressiveEffects: 200 * scale
}
component Anim: JsonObject {
property real mediaGifSpeedAdjustment: 300
property real sessionGifSpeed: 0.7
property AnimCurves curves: AnimCurves {}
property AnimDurations durations: AnimDurations {}
}
component Transparency: JsonObject {
property bool enabled: false
property real base: 0.85
property real layers: 0.4
}
component Anim: JsonObject {
property AnimCurves curves: AnimCurves {
}
property AnimDurations durations: AnimDurations {
}
property real mediaGifSpeedAdjustment: 300
property real sessionGifSpeed: 0.7
}
component AnimCurves: JsonObject {
property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1]
property list<real> standard: [0.2, 0, 0, 1, 1, 1]
property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
}
component AnimDurations: JsonObject {
property int expressiveDefaultSpatial: 500 * scale
property int expressiveEffects: 200 * scale
property int expressiveFastSpatial: 350 * scale
property int extraLarge: 1000 * scale
property int large: 600 * scale
property int normal: 400 * scale
property real scale: 1
property int small: 200 * scale
}
component FontFamily: JsonObject {
property string clock: "Rubik"
property string material: "Material Symbols Rounded"
property string mono: "CaskaydiaCove NF"
property string sans: "Segoe UI Variable Text"
}
component FontSize: JsonObject {
property int extraLarge: 28 * scale
property int large: 18 * scale
property int larger: 15 * scale
property int normal: 13 * scale
property real scale: 1
property int small: 11 * scale
property int smaller: 12 * scale
}
component FontStuff: JsonObject {
property FontFamily family: FontFamily {
}
property FontSize size: FontSize {
}
}
component Padding: JsonObject {
property int large: 15 * scale
property int larger: 12 * scale
property int normal: 10 * scale
property real scale: 1
property int small: 5 * scale
property int smaller: 7 * scale
}
component Rounding: JsonObject {
property int full: 1000 * scale
property int large: 25 * scale
property int normal: 17 * scale
property real scale: 1
property int small: 12 * scale
}
component Spacing: JsonObject {
property int large: 20 * scale
property int larger: 15 * scale
property int normal: 12 * scale
property real scale: 1
property int small: 7 * scale
property int smaller: 10 * scale
}
component Transparency: JsonObject {
property real base: 0.85
property bool enabled: false
property real layers: 0.4
}
}
+2 -2
View File
@@ -2,6 +2,6 @@ import Quickshell.Io
import qs.Config
JsonObject {
property int wallFadeDuration: MaterialEasing.standardTime
property bool enabled: true
property bool enabled: true
property int wallFadeDuration: MaterialEasing.standardTime
}
+6 -6
View File
@@ -2,9 +2,6 @@ import Quickshell.Io
JsonObject {
property bool autoHide: false
property int rounding: 8
property Popouts popouts: Popouts {}
property list<var> entries: [
{
id: "workspaces",
@@ -59,14 +56,17 @@ JsonObject {
enabled: true
},
]
property Popouts popouts: Popouts {
}
property int rounding: 8
component Popouts: JsonObject {
property bool tray: true
property bool audio: true
property bool activeWindow: true
property bool resources: true
property bool audio: true
property bool clock: true
property bool network: true
property bool resources: true
property bool tray: true
property bool upower: true
}
}
+309 -297
View File
@@ -4,27 +4,27 @@ import Quickshell
import Quickshell.Io
import ZShell
import QtQuick
import qs.Modules as Modules
import qs.Helpers
import qs.Paths
Singleton {
id: root
property alias background: adapter.background
property alias barConfig: adapter.barConfig
property alias lock: adapter.lock
property alias overview: adapter.overview
property alias services: adapter.services
property alias appearance: adapter.appearance
property alias background: adapter.background
property alias barConfig: adapter.barConfig
property alias colors: adapter.colors
property alias dashboard: adapter.dashboard
property alias general: adapter.general
property alias launcher: adapter.launcher
property alias lock: adapter.lock
property alias notifs: adapter.notifs
property alias osd: adapter.osd
property alias overview: adapter.overview
property bool recentlySaved: false
property alias services: adapter.services
property alias sidebar: adapter.sidebar
property alias utilities: adapter.utilities
property alias general: adapter.general
property alias dashboard: adapter.dashboard
property alias appearance: adapter.appearance
property alias osd: adapter.osd
property alias launcher: adapter.launcher
property alias colors: adapter.colors
function save(): void {
saveTimer.restart();
@@ -32,16 +32,266 @@ Singleton {
recentSaveCooldown.restart();
}
property bool recentlySaved: false
function serializeAppearance(): var {
return {
rounding: {
scale: appearance.rounding.scale
},
spacing: {
scale: appearance.spacing.scale
},
padding: {
scale: appearance.padding.scale
},
font: {
family: {
sans: appearance.font.family.sans,
mono: appearance.font.family.mono,
material: appearance.font.family.material,
clock: appearance.font.family.clock
},
size: {
scale: appearance.font.size.scale
}
},
anim: {
mediaGifSpeedAdjustment: 300,
sessionGifSpeed: 0.7,
durations: {
scale: appearance.anim.durations.scale
}
},
transparency: {
enabled: appearance.transparency.enabled,
base: appearance.transparency.base,
layers: appearance.transparency.layers
}
};
}
function serializeBackground(): var {
return {
wallFadeDuration: background.wallFadeDuration,
enabled: background.enabled
};
}
function serializeBar(): var {
return {
autoHide: barConfig.autoHide,
rounding: barConfig.rounding,
popouts: {
tray: barConfig.popouts.tray,
audio: barConfig.popouts.audio,
activeWindow: barConfig.popouts.activeWindow,
resources: barConfig.popouts.resources,
clock: barConfig.popouts.clock,
network: barConfig.popouts.network,
upower: barConfig.popouts.upower
},
entries: barConfig.entries
};
}
function serializeColors(): var {
return {
schemeType: colors.schemeType
};
}
function serializeConfig(): var {
return {
barConfig: serializeBar(),
lock: serializeLock(),
general: serializeGeneral(),
services: serializeServices(),
notifs: serializeNotifs(),
sidebar: serializeSidebar(),
utilities: serializeUtilities(),
dashboard: serializeDashboard(),
appearance: serializeAppearance(),
osd: serializeOsd(),
background: serializeBackground(),
launcher: serializeLauncher(),
colors: serializeColors()
};
}
function serializeDashboard(): var {
return {
enabled: dashboard.enabled,
mediaUpdateInterval: dashboard.mediaUpdateInterval,
dragThreshold: dashboard.dragThreshold,
sizes: {
tabIndicatorHeight: dashboard.sizes.tabIndicatorHeight,
tabIndicatorSpacing: dashboard.sizes.tabIndicatorSpacing,
infoWidth: dashboard.sizes.infoWidth,
infoIconSize: dashboard.sizes.infoIconSize,
dateTimeWidth: dashboard.sizes.dateTimeWidth,
mediaWidth: dashboard.sizes.mediaWidth,
mediaProgressSweep: dashboard.sizes.mediaProgressSweep,
mediaProgressThickness: dashboard.sizes.mediaProgressThickness,
resourceProgessThickness: dashboard.sizes.resourceProgessThickness,
weatherWidth: dashboard.sizes.weatherWidth,
mediaCoverArtSize: dashboard.sizes.mediaCoverArtSize,
mediaVisualiserSize: dashboard.sizes.mediaVisualiserSize,
resourceSize: dashboard.sizes.resourceSize
}
};
}
function serializeGeneral(): var {
return {
logo: general.logo,
wallpaperPath: general.wallpaperPath,
color: {
wallust: general.color.wallust,
mode: general.color.mode,
schemeGeneration: general.color.schemeGeneration,
scheduleDarkStart: general.color.scheduleDarkStart,
scheduleDarkEnd: general.color.scheduleDarkEnd,
neovimColors: general.color.neovimColors
},
apps: {
terminal: general.apps.terminal,
audio: general.apps.audio,
playback: general.apps.playback,
explorer: general.apps.explorer
},
idle: {
timouts: general.idle.timeouts
}
};
}
function serializeLauncher(): var {
return {
maxAppsShown: launcher.maxAppsShown,
maxWallpapers: launcher.maxWallpapers,
actionPrefix: launcher.actionPrefix,
specialPrefix: launcher.specialPrefix,
useFuzzy: {
apps: launcher.useFuzzy.apps,
actions: launcher.useFuzzy.actions,
schemes: launcher.useFuzzy.schemes,
variants: launcher.useFuzzy.variants,
wallpapers: launcher.useFuzzy.wallpapers
},
sizes: {
itemWidth: launcher.sizes.itemWidth,
itemHeight: launcher.sizes.itemHeight,
wallpaperWidth: launcher.sizes.wallpaperWidth,
wallpaperHeight: launcher.sizes.wallpaperHeight
},
actions: launcher.actions
};
}
function serializeLock(): var {
return {
recolorLogo: lock.recolorLogo,
enableFprint: lock.enableFprint,
maxFprintTries: lock.maxFprintTries,
blurAmount: lock.blurAmount,
sizes: {
heightMult: lock.sizes.heightMult,
ratio: lock.sizes.ratio,
centerWidth: lock.sizes.centerWidth
}
};
}
function serializeNotifs(): var {
return {
expire: notifs.expire,
defaultExpireTimeout: notifs.defaultExpireTimeout,
clearThreshold: notifs.clearThreshold,
expandThreshold: notifs.expandThreshold,
actionOnClick: notifs.actionOnClick,
groupPreviewNum: notifs.groupPreviewNum,
sizes: {
width: notifs.sizes.width,
image: notifs.sizes.image,
badge: notifs.sizes.badge
}
};
}
function serializeOsd(): var {
return {
enabled: osd.enabled,
hideDelay: osd.hideDelay,
enableBrightness: osd.enableBrightness,
enableMicrophone: osd.enableMicrophone,
allMonBrightness: osd.allMonBrightness,
sizes: {
sliderWidth: osd.sizes.sliderWidth,
sliderHeight: osd.sizes.sliderHeight
}
};
}
function serializeServices(): var {
return {
weatherLocation: services.weatherLocation,
useFahrenheit: services.useFahrenheit,
useTwelveHourClock: services.useTwelveHourClock,
gpuType: services.gpuType,
audioIncrement: services.audioIncrement,
brightnessIncrement: services.brightnessIncrement,
maxVolume: services.maxVolume,
defaultPlayer: services.defaultPlayer,
playerAliases: services.playerAliases
};
}
function serializeSidebar(): var {
return {
enabled: sidebar.enabled,
sizes: {
width: sidebar.sizes.width
}
};
}
function serializeUtilities(): var {
return {
enabled: utilities.enabled,
maxToasts: utilities.maxToasts,
sizes: {
width: utilities.sizes.width,
toastWidth: utilities.sizes.toastWidth
},
toasts: {
configLoaded: utilities.toasts.configLoaded,
chargingChanged: utilities.toasts.chargingChanged,
gameModeChanged: utilities.toasts.gameModeChanged,
dndChanged: utilities.toasts.dndChanged,
audioOutputChanged: utilities.toasts.audioOutputChanged,
audioInputChanged: utilities.toasts.audioInputChanged,
capsLockChanged: utilities.toasts.capsLockChanged,
numLockChanged: utilities.toasts.numLockChanged,
kbLayoutChanged: utilities.toasts.kbLayoutChanged,
vpnChanged: utilities.toasts.vpnChanged,
nowPlaying: utilities.toasts.nowPlaying
},
vpn: {
enabled: utilities.vpn.enabled,
provider: utilities.vpn.provider
}
};
}
ElapsedTimer {
id: timer
}
Timer {
id: saveTimer
interval: 500
onTriggered: {
timer.restart();
try {
@@ -65,316 +315,78 @@ Singleton {
id: recentSaveCooldown
interval: 2000
onTriggered: {
root.recentlySaved = false;
}
}
function serializeConfig(): var {
return {
barConfig: serializeBar(),
lock: serializeLock(),
general: serializeGeneral(),
services: serializeServices(),
notifs: serializeNotifs(),
sidebar: serializeSidebar(),
utilities: serializeUtilities(),
dashboard: serializeDashboard(),
appearance: serializeAppearance(),
osd: serializeOsd(),
background: serializeBackground(),
launcher: serializeLauncher(),
colors: serializeColors()
}
}
FileView {
id: fileView
function serializeBar(): var {
return {
autoHide: barConfig.autoHide,
rounding: barConfig.rounding,
popouts: {
tray: barConfig.popouts.tray,
audio: barConfig.popouts.audio,
activeWindow: barConfig.popouts.activeWindow,
resources: barConfig.popouts.resources,
clock: barConfig.popouts.clock,
network: barConfig.popouts.network,
upower: barConfig.popouts.upower
},
entries: barConfig.entries
}
}
path: `${Paths.config}/config.json`
watchChanges: true
function serializeLock(): var {
return {
recolorLogo: lock.recolorLogo,
enableFprint: lock.enableFprint,
maxFprintTries: lock.maxFprintTries,
blurAmount: lock.blurAmount,
sizes: {
heightMult: lock.sizes.heightMult,
ratio: lock.sizes.ratio,
centerWidth: lock.sizes.centerWidth
}
};
}
function serializeGeneral(): var {
return {
logo: general.logo,
wallpaperPath: general.wallpaperPath,
color: {
wallust: general.color.wallust,
mode: general.color.mode,
schemeGeneration: general.color.schemeGeneration,
scheduleDarkStart: general.color.scheduleDarkStart,
scheduleDarkEnd: general.color.scheduleDarkEnd,
neovimColors: general.color.neovimColors
},
apps: {
terminal: general.apps.terminal,
audio: general.apps.audio,
playback: general.apps.playback,
explorer: general.apps.explorer,
},
idle: {
timouts: general.idle.timeouts
}
}
}
function serializeServices(): var {
return {
weatherLocation: services.weatherLocation,
useFahrenheit: services.useFahrenheit,
useTwelveHourClock: services.useTwelveHourClock,
gpuType: services.gpuType,
audioIncrement: services.audioIncrement,
brightnessIncrement: services.brightnessIncrement,
maxVolume: services.maxVolume,
defaultPlayer: services.defaultPlayer,
playerAliases: services.playerAliases
};
}
function serializeNotifs(): var {
return {
expire: notifs.expire,
defaultExpireTimeout: notifs.defaultExpireTimeout,
clearThreshold: notifs.clearThreshold,
expandThreshold: notifs.expandThreshold,
actionOnClick: notifs.actionOnClick,
groupPreviewNum: notifs.groupPreviewNum,
sizes: {
width: notifs.sizes.width,
image: notifs.sizes.image,
badge: notifs.sizes.badge
}
};
}
function serializeSidebar(): var {
return {
enabled: sidebar.enabled,
sizes: {
width: sidebar.sizes.width
}
};
}
function serializeUtilities(): var {
return {
enabled: utilities.enabled,
maxToasts: utilities.maxToasts,
sizes: {
width: utilities.sizes.width,
toastWidth: utilities.sizes.toastWidth
},
toasts: {
configLoaded: utilities.toasts.configLoaded,
chargingChanged: utilities.toasts.chargingChanged,
gameModeChanged: utilities.toasts.gameModeChanged,
dndChanged: utilities.toasts.dndChanged,
audioOutputChanged: utilities.toasts.audioOutputChanged,
audioInputChanged: utilities.toasts.audioInputChanged,
capsLockChanged: utilities.toasts.capsLockChanged,
numLockChanged: utilities.toasts.numLockChanged,
kbLayoutChanged: utilities.toasts.kbLayoutChanged,
vpnChanged: utilities.toasts.vpnChanged,
nowPlaying: utilities.toasts.nowPlaying
},
vpn: {
enabled: utilities.vpn.enabled,
provider: utilities.vpn.provider
}
};
}
function serializeDashboard(): var {
return {
enabled: dashboard.enabled,
mediaUpdateInterval: dashboard.mediaUpdateInterval,
dragThreshold: dashboard.dragThreshold,
sizes: {
tabIndicatorHeight: dashboard.sizes.tabIndicatorHeight,
tabIndicatorSpacing: dashboard.sizes.tabIndicatorSpacing,
infoWidth: dashboard.sizes.infoWidth,
infoIconSize: dashboard.sizes.infoIconSize,
dateTimeWidth: dashboard.sizes.dateTimeWidth,
mediaWidth: dashboard.sizes.mediaWidth,
mediaProgressSweep: dashboard.sizes.mediaProgressSweep,
mediaProgressThickness: dashboard.sizes.mediaProgressThickness,
resourceProgessThickness: dashboard.sizes.resourceProgessThickness,
weatherWidth: dashboard.sizes.weatherWidth,
mediaCoverArtSize: dashboard.sizes.mediaCoverArtSize,
mediaVisualiserSize: dashboard.sizes.mediaVisualiserSize,
resourceSize: dashboard.sizes.resourceSize
}
};
}
function serializeOsd(): var {
return {
enabled: osd.enabled,
hideDelay: osd.hideDelay,
enableBrightness: osd.enableBrightness,
enableMicrophone: osd.enableMicrophone,
allMonBrightness: osd.allMonBrightness,
sizes: {
sliderWidth: osd.sizes.sliderWidth,
sliderHeight: osd.sizes.sliderHeight
}
};
}
function serializeLauncher(): var {
return {
maxAppsShown: launcher.maxAppsShown,
maxWallpapers: launcher.maxWallpapers,
actionPrefix: launcher.actionPrefix,
specialPrefix: launcher.specialPrefix,
useFuzzy: {
apps: launcher.useFuzzy.apps,
actions: launcher.useFuzzy.actions,
schemes: launcher.useFuzzy.schemes,
variants: launcher.useFuzzy.variants,
wallpapers: launcher.useFuzzy.wallpapers
},
sizes: {
itemWidth: launcher.sizes.itemWidth,
itemHeight: launcher.sizes.itemHeight,
wallpaperWidth: launcher.sizes.wallpaperWidth,
wallpaperHeight: launcher.sizes.wallpaperHeight
},
actions: launcher.actions
}
}
function serializeBackground(): var {
return {
wallFadeDuration: background.wallFadeDuration,
enabled: background.enabled
}
}
function serializeAppearance(): var {
return {
rounding: {
scale: appearance.rounding.scale
},
spacing: {
scale: appearance.spacing.scale
},
padding: {
scale: appearance.padding.scale
},
font: {
family: {
sans: appearance.font.family.sans,
mono: appearance.font.family.mono,
material: appearance.font.family.material,
clock: appearance.font.family.clock
},
size: {
scale: appearance.font.size.scale
}
},
anim: {
mediaGifSpeedAdjustment: 300,
sessionGifSpeed: 0.7,
durations: {
scale: appearance.anim.durations.scale
}
},
transparency: {
enabled: appearance.transparency.enabled,
base: appearance.transparency.base,
layers: appearance.transparency.layers
}
};
}
function serializeColors(): var {
return {
schemeType: colors.schemeType,
}
}
FileView {
id: fileView
path: `${Paths.config}/config.json`
watchChanges: true
onFileChanged: {
if ( !root.recentlySaved ) {
onFileChanged: {
if (!root.recentlySaved) {
timer.restart();
reload();
} else {
reload();
}
}
onLoadFailed: err => {
if (err !== FileViewError.FileNotFound)
Toaster.toast(qsTr("Failed to read config"), FileViewError.toString(err), "settings_alert", Toast.Warning);
}
onLoaded: {
ModeScheduler.checkStartup();
try {
JSON.parse(text());
const elapsed = timer.elapsedMs();
if ( adapter.utilities.toasts.configLoaded && !root.recentlySaved ) {
if (adapter.utilities.toasts.configLoaded && !root.recentlySaved) {
Toaster.toast(qsTr("Config loaded"), qsTr("Config loaded in %1ms").arg(elapsed), "rule_settings");
} else if ( adapter.utilities.toasts.configLoaded && root.recentlySaved ) {
} else if (adapter.utilities.toasts.configLoaded && root.recentlySaved) {
Toaster.toast(qsTr("Config saved"), qsTr("Config reloaded in %1ms").arg(elapsed), "settings_alert");
}
} catch (e) {
Toaster.toast(qsTr("Failed to load config"), e.message, "settings_alert", Toast.Error);
}
}
onLoadFailed: err => {
if ( err !== FileViewError.FileNotFound )
Toaster.toast(qsTr("Failed to read config"), FileViewError.toString(err), "settings_alert", Toast.Warning);
}
onSaveFailed: err => Toaster.toast(qsTr("Failed to save config"), FileViewError.toString(err), "settings_alert", Toast.Error)
JsonAdapter {
id: adapter
property BackgroundConfig background: BackgroundConfig {}
property BarConfig barConfig: BarConfig {}
property LockConf lock: LockConf {}
property Overview overview: Overview {}
property Services services: Services {}
property NotifConfig notifs: NotifConfig {}
property SidebarConfig sidebar: SidebarConfig {}
property UtilConfig utilities: UtilConfig {}
property General general: General {}
property DashboardConfig dashboard: DashboardConfig {}
property AppearanceConf appearance: AppearanceConf {}
property Osd osd: Osd {}
property Launcher launcher: Launcher {}
property Colors colors: Colors {}
}
}
JsonAdapter {
id: adapter
property AppearanceConf appearance: AppearanceConf {
}
property BackgroundConfig background: BackgroundConfig {
}
property BarConfig barConfig: BarConfig {
}
property Colors colors: Colors {
}
property DashboardConfig dashboard: DashboardConfig {
}
property General general: General {
}
property Launcher launcher: Launcher {
}
property LockConf lock: LockConf {
}
property NotifConfig notifs: NotifConfig {
}
property Osd osd: Osd {
}
property Overview overview: Overview {
}
property Services services: Services {
}
property SidebarConfig sidebar: SidebarConfig {
}
property UtilConfig utilities: UtilConfig {
}
}
}
}
+20 -19
View File
@@ -1,24 +1,25 @@
import Quickshell.Io
JsonObject {
property bool enabled: true
property int mediaUpdateInterval: 500
property int dragThreshold: 50
property Sizes sizes: Sizes {}
property int dragThreshold: 50
property bool enabled: true
property int mediaUpdateInterval: 500
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
readonly property int tabIndicatorHeight: 3
readonly property int tabIndicatorSpacing: 5
readonly property int infoWidth: 200
readonly property int infoIconSize: 25
readonly property int dateTimeWidth: 110
readonly property int mediaWidth: 200
readonly property int mediaProgressSweep: 180
readonly property int mediaProgressThickness: 8
readonly property int resourceProgessThickness: 10
readonly property int weatherWidth: 250
readonly property int mediaCoverArtSize: 150
readonly property int mediaVisualiserSize: 80
readonly property int resourceSize: 200
}
component Sizes: JsonObject {
readonly property int dateTimeWidth: 110
readonly property int infoIconSize: 25
readonly property int infoWidth: 200
readonly property int mediaCoverArtSize: 150
readonly property int mediaProgressSweep: 180
readonly property int mediaProgressThickness: 8
readonly property int mediaVisualiserSize: 80
readonly property int mediaWidth: 200
readonly property int resourceProgessThickness: 10
readonly property int resourceSize: 200
readonly property int tabIndicatorHeight: 3
readonly property int tabIndicatorSpacing: 5
readonly property int weatherWidth: 250
}
}
+235 -233
View File
@@ -9,270 +9,272 @@ import qs.Helpers
import qs.Paths
Singleton {
id: root
id: root
property bool showPreview
property string scheme
property string flavour
readonly property bool light: showPreview ? previewLight : currentLight
property bool currentLight
property bool previewLight
readonly property M3Palette palette: showPreview ? preview : current
readonly property M3TPalette tPalette: M3TPalette {}
readonly property M3Palette current: M3Palette {}
readonly property M3Palette preview: M3Palette {}
readonly property Transparency transparency: Transparency {}
readonly property alias wallLuminance: analyser.luminance
readonly property M3Palette current: M3Palette {
}
property bool currentLight
property string flavour
readonly property bool light: showPreview ? previewLight : currentLight
readonly property M3Palette palette: showPreview ? preview : current
readonly property M3Palette preview: M3Palette {
}
property bool previewLight
property string scheme
property bool showPreview
readonly property M3TPalette tPalette: M3TPalette {
}
readonly property Transparency transparency: Transparency {
}
readonly property alias wallLuminance: analyser.luminance
function getLuminance(c: color): real {
if (c.r == 0 && c.g == 0 && c.b == 0)
return 0;
return Math.sqrt(0.299 * (c.r ** 2) + 0.587 * (c.g ** 2) + 0.114 * (c.b ** 2));
}
function alterColor(c: color, a: real, layer: int): color {
const luminance = getLuminance(c);
function alterColor(c: color, a: real, layer: int): color {
const luminance = getLuminance(c);
const offset = (!light || layer == 1 ? 1 : -layer / 2) * (light ? 0.2 : 0.3) * (1 - transparency.base) * (1 + wallLuminance * (light ? (layer == 1 ? 3 : 1) : 2.5));
const scale = (luminance + offset) / luminance;
const r = Math.max(0, Math.min(1, c.r * scale));
const g = Math.max(0, Math.min(1, c.g * scale));
const b = Math.max(0, Math.min(1, c.b * scale));
const offset = (!light || layer == 1 ? 1 : -layer / 2) * (light ? 0.2 : 0.3) * (1 - transparency.base) * (1 + wallLuminance * (light ? (layer == 1 ? 3 : 1) : 2.5));
const scale = (luminance + offset) / luminance;
const r = Math.max(0, Math.min(1, c.r * scale));
const g = Math.max(0, Math.min(1, c.g * scale));
const b = Math.max(0, Math.min(1, c.b * scale));
return Qt.rgba(r, g, b, a);
}
return Qt.rgba(r, g, b, a);
}
function getLuminance(c: color): real {
if (c.r == 0 && c.g == 0 && c.b == 0)
return 0;
return Math.sqrt(0.299 * (c.r ** 2) + 0.587 * (c.g ** 2) + 0.114 * (c.b ** 2));
}
function layer(c: color, layer: var): color {
if (!transparency.enabled)
return c;
function layer(c: color, layer: var): color {
if (!transparency.enabled)
return c;
return layer === 0 ? Qt.alpha(c, transparency.base) : alterColor(c, transparency.layers, layer ?? 1);
}
return layer === 0 ? Qt.alpha(c, transparency.base) : alterColor(c, transparency.layers, layer ?? 1);
}
function on(c: color): color {
if (c.hslLightness < 0.5)
return Qt.hsla(c.hslHue, c.hslSaturation, 0.9, 1);
return Qt.hsla(c.hslHue, c.hslSaturation, 0.1, 1);
}
function load(data: string, isPreview: bool): void {
const colors = isPreview ? preview : current;
const scheme = JSON.parse(data);
function load(data: string, isPreview: bool): void {
const colors = isPreview ? preview : current;
const scheme = JSON.parse(data);
if (!isPreview) {
root.scheme = scheme.name;
flavour = scheme.flavor;
currentLight = scheme.mode === "light";
} else {
previewLight = scheme.mode === "light";
}
if (!isPreview) {
root.scheme = scheme.name;
flavour = scheme.flavor;
currentLight = scheme.mode === "light";
} else {
previewLight = scheme.mode === "light";
}
for (const [name, color] of Object.entries(scheme.colors)) {
const propName = name.startsWith("term") ? name : `m3${name}`;
if (colors.hasOwnProperty(propName))
colors[propName] = `${color}`;
}
}
for (const [name, color] of Object.entries(scheme.colors)) {
const propName = name.startsWith("term") ? name : `m3${name}`;
if (colors.hasOwnProperty(propName))
colors[propName] = `${color}`;
}
}
function on(c: color): color {
if (c.hslLightness < 0.5)
return Qt.hsla(c.hslHue, c.hslSaturation, 0.9, 1);
return Qt.hsla(c.hslHue, c.hslSaturation, 0.1, 1);
}
FileView {
path: `${Paths.state}/scheme.json`
watchChanges: true
onFileChanged: reload()
onLoaded: root.load(text(), false)
}
FileView {
path: `${Paths.state}/scheme.json`
watchChanges: true
ImageAnalyser {
id: analyser
onFileChanged: reload()
onLoaded: root.load(text(), false)
}
source: WallpaperPath.currentWallpaperPath
}
ImageAnalyser {
id: analyser
component Transparency: QtObject {
readonly property bool enabled: Appearance.transparency.enabled
readonly property real base: Appearance.transparency.base - (root.light ? 0.1 : 0)
readonly property real layers: Appearance.transparency.layers
}
component M3TPalette: QtObject {
readonly property color m3primary_paletteKeyColor: root.layer(root.palette.m3primary_paletteKeyColor)
readonly property color m3secondary_paletteKeyColor: root.layer(root.palette.m3secondary_paletteKeyColor)
readonly property color m3tertiary_paletteKeyColor: root.layer(root.palette.m3tertiary_paletteKeyColor)
readonly property color m3neutral_paletteKeyColor: root.layer(root.palette.m3neutral_paletteKeyColor)
readonly property color m3neutral_variant_paletteKeyColor: root.layer(root.palette.m3neutral_variant_paletteKeyColor)
readonly property color m3background: root.layer(root.palette.m3background, 0)
readonly property color m3onBackground: root.layer(root.palette.m3onBackground)
readonly property color m3surface: root.layer(root.palette.m3surface, 0)
readonly property color m3surfaceDim: root.layer(root.palette.m3surfaceDim, 0)
readonly property color m3surfaceBright: root.layer(root.palette.m3surfaceBright, 0)
readonly property color m3surfaceContainerLowest: root.layer(root.palette.m3surfaceContainerLowest)
readonly property color m3surfaceContainerLow: root.layer(root.palette.m3surfaceContainerLow)
readonly property color m3surfaceContainer: root.layer(root.palette.m3surfaceContainer)
readonly property color m3surfaceContainerHigh: root.layer(root.palette.m3surfaceContainerHigh)
readonly property color m3surfaceContainerHighest: root.layer(root.palette.m3surfaceContainerHighest)
readonly property color m3onSurface: root.layer(root.palette.m3onSurface)
readonly property color m3surfaceVariant: root.layer(root.palette.m3surfaceVariant, 0)
readonly property color m3onSurfaceVariant: root.layer(root.palette.m3onSurfaceVariant)
readonly property color m3inverseSurface: root.layer(root.palette.m3inverseSurface, 0)
readonly property color m3inverseOnSurface: root.layer(root.palette.m3inverseOnSurface)
readonly property color m3outline: root.layer(root.palette.m3outline)
readonly property color m3outlineVariant: root.layer(root.palette.m3outlineVariant)
readonly property color m3shadow: root.layer(root.palette.m3shadow)
readonly property color m3scrim: root.layer(root.palette.m3scrim)
readonly property color m3surfaceTint: root.layer(root.palette.m3surfaceTint)
readonly property color m3primary: root.layer(root.palette.m3primary)
readonly property color m3onPrimary: root.layer(root.palette.m3onPrimary)
readonly property color m3primaryContainer: root.layer(root.palette.m3primaryContainer)
readonly property color m3onPrimaryContainer: root.layer(root.palette.m3onPrimaryContainer)
readonly property color m3inversePrimary: root.layer(root.palette.m3inversePrimary)
readonly property color m3secondary: root.layer(root.palette.m3secondary)
readonly property color m3onSecondary: root.layer(root.palette.m3onSecondary)
readonly property color m3secondaryContainer: root.layer(root.palette.m3secondaryContainer)
readonly property color m3onSecondaryContainer: root.layer(root.palette.m3onSecondaryContainer)
readonly property color m3tertiary: root.layer(root.palette.m3tertiary)
readonly property color m3onTertiary: root.layer(root.palette.m3onTertiary)
readonly property color m3tertiaryContainer: root.layer(root.palette.m3tertiaryContainer)
readonly property color m3onTertiaryContainer: root.layer(root.palette.m3onTertiaryContainer)
readonly property color m3error: root.layer(root.palette.m3error)
readonly property color m3onError: root.layer(root.palette.m3onError)
readonly property color m3errorContainer: root.layer(root.palette.m3errorContainer)
readonly property color m3onErrorContainer: root.layer(root.palette.m3onErrorContainer)
readonly property color m3success: root.layer(root.palette.m3success)
readonly property color m3onSuccess: root.layer(root.palette.m3onSuccess)
readonly property color m3successContainer: root.layer(root.palette.m3successContainer)
readonly property color m3onSuccessContainer: root.layer(root.palette.m3onSuccessContainer)
readonly property color m3primaryFixed: root.layer(root.palette.m3primaryFixed)
readonly property color m3primaryFixedDim: root.layer(root.palette.m3primaryFixedDim)
readonly property color m3onPrimaryFixed: root.layer(root.palette.m3onPrimaryFixed)
readonly property color m3onPrimaryFixedVariant: root.layer(root.palette.m3onPrimaryFixedVariant)
readonly property color m3secondaryFixed: root.layer(root.palette.m3secondaryFixed)
readonly property color m3secondaryFixedDim: root.layer(root.palette.m3secondaryFixedDim)
readonly property color m3onSecondaryFixed: root.layer(root.palette.m3onSecondaryFixed)
readonly property color m3onSecondaryFixedVariant: root.layer(root.palette.m3onSecondaryFixedVariant)
readonly property color m3tertiaryFixed: root.layer(root.palette.m3tertiaryFixed)
readonly property color m3tertiaryFixedDim: root.layer(root.palette.m3tertiaryFixedDim)
readonly property color m3onTertiaryFixed: root.layer(root.palette.m3onTertiaryFixed)
readonly property color m3onTertiaryFixedVariant: root.layer(root.palette.m3onTertiaryFixedVariant)
}
component M3Palette: QtObject {
property color m3primary_paletteKeyColor: "#a8627b"
property color m3secondary_paletteKeyColor: "#8e6f78"
property color m3tertiary_paletteKeyColor: "#986e4c"
property color m3neutral_paletteKeyColor: "#807477"
property color m3neutral_variant_paletteKeyColor: "#837377"
property color m3background: "#191114"
property color m3onBackground: "#efdfe2"
property color m3surface: "#191114"
property color m3surfaceDim: "#191114"
property color m3surfaceBright: "#403739"
property color m3surfaceContainerLowest: "#130c0e"
property color m3surfaceContainerLow: "#22191c"
property color m3surfaceContainer: "#261d20"
property color m3surfaceContainerHigh: "#31282a"
property color m3surfaceContainerHighest: "#3c3235"
property color m3onSurface: "#efdfe2"
property color m3surfaceVariant: "#514347"
property color m3onSurfaceVariant: "#d5c2c6"
property color m3inverseSurface: "#efdfe2"
property color m3inverseOnSurface: "#372e30"
property color m3outline: "#9e8c91"
property color m3outlineVariant: "#514347"
property color m3shadow: "#000000"
property color m3scrim: "#000000"
property color m3surfaceTint: "#ffb0ca"
property color m3primary: "#ffb0ca"
property color m3onPrimary: "#541d34"
property color m3primaryContainer: "#6f334a"
property color m3onPrimaryContainer: "#ffd9e3"
property color m3inversePrimary: "#8b4a62"
property color m3secondary: "#e2bdc7"
property color m3onSecondary: "#422932"
property color m3secondaryContainer: "#5a3f48"
property color m3onSecondaryContainer: "#ffd9e3"
property color m3tertiary: "#f0bc95"
property color m3onTertiary: "#48290c"
property color m3tertiaryContainer: "#b58763"
property color m3onTertiaryContainer: "#000000"
property color m3error: "#ffb4ab"
property color m3onError: "#690005"
property color m3errorContainer: "#93000a"
property color m3onErrorContainer: "#ffdad6"
property color m3success: "#B5CCBA"
property color m3onSuccess: "#213528"
property color m3successContainer: "#374B3E"
property color m3onSuccessContainer: "#D1E9D6"
property color m3primaryFixed: "#ffd9e3"
property color m3primaryFixedDim: "#ffb0ca"
property color m3onPrimaryFixed: "#39071f"
property color m3onPrimaryFixedVariant: "#6f334a"
property color m3secondaryFixed: "#ffd9e3"
property color m3secondaryFixedDim: "#e2bdc7"
property color m3onSecondaryFixed: "#2b151d"
property color m3onSecondaryFixedVariant: "#5a3f48"
property color m3tertiaryFixed: "#ffdcc3"
property color m3tertiaryFixedDim: "#f0bc95"
property color m3onTertiaryFixed: "#2f1500"
property color m3onTertiaryFixedVariant: "#623f21"
}
source: WallpaperPath.currentWallpaperPath
}
component M3MaccchiatoPalette: QtObject {
property color m3primary_paletteKeyColor: "#6a73ac"
property color m3secondary_paletteKeyColor: "#72758e"
property color m3tertiary_paletteKeyColor: "#9b6592"
property color m3background: "#131317"
property color m3error: "#ffb4ab"
property color m3errorContainer: "#93000a"
property color m3inverseOnSurface: "#303034"
property color m3inversePrimary: "#525b92"
property color m3inverseSurface: "#e4e1e7"
property color m3neutral_paletteKeyColor: "#77767b"
property color m3neutral_variant_paletteKeyColor: "#767680"
property color m3background: "#131317"
property color m3onBackground: "#e4e1e7"
property color m3onError: "#690005"
property color m3onErrorContainer: "#ffdad6"
property color m3onPrimary: "#232c60"
property color m3onPrimaryContainer: "#ffffff"
property color m3onPrimaryFixed: "#0b154b"
property color m3onPrimaryFixedVariant: "#3a4378"
property color m3onSecondary: "#2c2f44"
property color m3onSecondaryContainer: "#b1b3ce"
property color m3onSecondaryFixed: "#171a2e"
property color m3onSecondaryFixedVariant: "#42455c"
property color m3onSuccess: "#213528"
property color m3onSuccessContainer: "#D1E9D6"
property color m3onSurface: "#e4e1e7"
property color m3onSurfaceVariant: "#c6c5d1"
property color m3onTertiary: "#4c1f48"
property color m3onTertiaryContainer: "#000000"
property color m3onTertiaryFixed: "#340831"
property color m3onTertiaryFixedVariant: "#66365f"
property color m3outline: "#90909a"
property color m3outlineVariant: "#46464f"
property color m3primary: "#bac3ff"
property color m3primaryContainer: "#6a73ac"
property color m3primaryFixed: "#dee0ff"
property color m3primaryFixedDim: "#bac3ff"
property color m3primary_paletteKeyColor: "#6a73ac"
property color m3scrim: "#000000"
property color m3secondary: "#c3c5e0"
property color m3secondaryContainer: "#42455c"
property color m3secondaryFixed: "#dfe1fd"
property color m3secondaryFixedDim: "#c3c5e0"
property color m3secondary_paletteKeyColor: "#72758e"
property color m3shadow: "#000000"
property color m3success: "#B5CCBA"
property color m3successContainer: "#374B3E"
property color m3surface: "#131317"
property color m3surfaceDim: "#131317"
property color m3surfaceBright: "#39393d"
property color m3surfaceContainerLowest: "#0e0e12"
property color m3surfaceContainerLow: "#1b1b1f"
property color m3surfaceContainer: "#1f1f23"
property color m3surfaceContainerHigh: "#2a2a2e"
property color m3surfaceContainerHighest: "#353438"
property color m3onSurface: "#e4e1e7"
property color m3surfaceVariant: "#46464f"
property color m3onSurfaceVariant: "#c6c5d1"
property color m3inverseSurface: "#e4e1e7"
property color m3inverseOnSurface: "#303034"
property color m3outline: "#90909a"
property color m3outlineVariant: "#46464f"
property color m3shadow: "#000000"
property color m3scrim: "#000000"
property color m3surfaceContainerLow: "#1b1b1f"
property color m3surfaceContainerLowest: "#0e0e12"
property color m3surfaceDim: "#131317"
property color m3surfaceTint: "#bac3ff"
property color m3primary: "#bac3ff"
property color m3onPrimary: "#232c60"
property color m3primaryContainer: "#6a73ac"
property color m3onPrimaryContainer: "#ffffff"
property color m3inversePrimary: "#525b92"
property color m3secondary: "#c3c5e0"
property color m3onSecondary: "#2c2f44"
property color m3secondaryContainer: "#42455c"
property color m3onSecondaryContainer: "#b1b3ce"
property color m3surfaceVariant: "#46464f"
property color m3tertiary: "#f1b3e5"
property color m3onTertiary: "#4c1f48"
property color m3tertiaryContainer: "#b77ead"
property color m3onTertiaryContainer: "#000000"
property color m3error: "#ffb4ab"
property color m3onError: "#690005"
property color m3errorContainer: "#93000a"
property color m3onErrorContainer: "#ffdad6"
property color m3primaryFixed: "#dee0ff"
property color m3primaryFixedDim: "#bac3ff"
property color m3onPrimaryFixed: "#0b154b"
property color m3onPrimaryFixedVariant: "#3a4378"
property color m3secondaryFixed: "#dfe1fd"
property color m3secondaryFixedDim: "#c3c5e0"
property color m3onSecondaryFixed: "#171a2e"
property color m3onSecondaryFixedVariant: "#42455c"
property color m3tertiaryFixed: "#ffd7f4"
property color m3tertiaryFixedDim: "#f1b3e5"
property color m3onTertiaryFixed: "#340831"
property color m3onTertiaryFixedVariant: "#66365f"
property color m3success: "#B5CCBA"
property color m3tertiary_paletteKeyColor: "#9b6592"
}
component M3Palette: QtObject {
property color m3background: "#191114"
property color m3error: "#ffb4ab"
property color m3errorContainer: "#93000a"
property color m3inverseOnSurface: "#372e30"
property color m3inversePrimary: "#8b4a62"
property color m3inverseSurface: "#efdfe2"
property color m3neutral_paletteKeyColor: "#807477"
property color m3neutral_variant_paletteKeyColor: "#837377"
property color m3onBackground: "#efdfe2"
property color m3onError: "#690005"
property color m3onErrorContainer: "#ffdad6"
property color m3onPrimary: "#541d34"
property color m3onPrimaryContainer: "#ffd9e3"
property color m3onPrimaryFixed: "#39071f"
property color m3onPrimaryFixedVariant: "#6f334a"
property color m3onSecondary: "#422932"
property color m3onSecondaryContainer: "#ffd9e3"
property color m3onSecondaryFixed: "#2b151d"
property color m3onSecondaryFixedVariant: "#5a3f48"
property color m3onSuccess: "#213528"
property color m3successContainer: "#374B3E"
property color m3onSuccessContainer: "#D1E9D6"
property color m3onSurface: "#efdfe2"
property color m3onSurfaceVariant: "#d5c2c6"
property color m3onTertiary: "#48290c"
property color m3onTertiaryContainer: "#000000"
property color m3onTertiaryFixed: "#2f1500"
property color m3onTertiaryFixedVariant: "#623f21"
property color m3outline: "#9e8c91"
property color m3outlineVariant: "#514347"
property color m3primary: "#ffb0ca"
property color m3primaryContainer: "#6f334a"
property color m3primaryFixed: "#ffd9e3"
property color m3primaryFixedDim: "#ffb0ca"
property color m3primary_paletteKeyColor: "#a8627b"
property color m3scrim: "#000000"
property color m3secondary: "#e2bdc7"
property color m3secondaryContainer: "#5a3f48"
property color m3secondaryFixed: "#ffd9e3"
property color m3secondaryFixedDim: "#e2bdc7"
property color m3secondary_paletteKeyColor: "#8e6f78"
property color m3shadow: "#000000"
property color m3success: "#B5CCBA"
property color m3successContainer: "#374B3E"
property color m3surface: "#191114"
property color m3surfaceBright: "#403739"
property color m3surfaceContainer: "#261d20"
property color m3surfaceContainerHigh: "#31282a"
property color m3surfaceContainerHighest: "#3c3235"
property color m3surfaceContainerLow: "#22191c"
property color m3surfaceContainerLowest: "#130c0e"
property color m3surfaceDim: "#191114"
property color m3surfaceTint: "#ffb0ca"
property color m3surfaceVariant: "#514347"
property color m3tertiary: "#f0bc95"
property color m3tertiaryContainer: "#b58763"
property color m3tertiaryFixed: "#ffdcc3"
property color m3tertiaryFixedDim: "#f0bc95"
property color m3tertiary_paletteKeyColor: "#986e4c"
}
component M3TPalette: QtObject {
readonly property color m3background: root.layer(root.palette.m3background, 0)
readonly property color m3error: root.layer(root.palette.m3error)
readonly property color m3errorContainer: root.layer(root.palette.m3errorContainer)
readonly property color m3inverseOnSurface: root.layer(root.palette.m3inverseOnSurface)
readonly property color m3inversePrimary: root.layer(root.palette.m3inversePrimary)
readonly property color m3inverseSurface: root.layer(root.palette.m3inverseSurface, 0)
readonly property color m3neutral_paletteKeyColor: root.layer(root.palette.m3neutral_paletteKeyColor)
readonly property color m3neutral_variant_paletteKeyColor: root.layer(root.palette.m3neutral_variant_paletteKeyColor)
readonly property color m3onBackground: root.layer(root.palette.m3onBackground)
readonly property color m3onError: root.layer(root.palette.m3onError)
readonly property color m3onErrorContainer: root.layer(root.palette.m3onErrorContainer)
readonly property color m3onPrimary: root.layer(root.palette.m3onPrimary)
readonly property color m3onPrimaryContainer: root.layer(root.palette.m3onPrimaryContainer)
readonly property color m3onPrimaryFixed: root.layer(root.palette.m3onPrimaryFixed)
readonly property color m3onPrimaryFixedVariant: root.layer(root.palette.m3onPrimaryFixedVariant)
readonly property color m3onSecondary: root.layer(root.palette.m3onSecondary)
readonly property color m3onSecondaryContainer: root.layer(root.palette.m3onSecondaryContainer)
readonly property color m3onSecondaryFixed: root.layer(root.palette.m3onSecondaryFixed)
readonly property color m3onSecondaryFixedVariant: root.layer(root.palette.m3onSecondaryFixedVariant)
readonly property color m3onSuccess: root.layer(root.palette.m3onSuccess)
readonly property color m3onSuccessContainer: root.layer(root.palette.m3onSuccessContainer)
readonly property color m3onSurface: root.layer(root.palette.m3onSurface)
readonly property color m3onSurfaceVariant: root.layer(root.palette.m3onSurfaceVariant)
readonly property color m3onTertiary: root.layer(root.palette.m3onTertiary)
readonly property color m3onTertiaryContainer: root.layer(root.palette.m3onTertiaryContainer)
readonly property color m3onTertiaryFixed: root.layer(root.palette.m3onTertiaryFixed)
readonly property color m3onTertiaryFixedVariant: root.layer(root.palette.m3onTertiaryFixedVariant)
readonly property color m3outline: root.layer(root.palette.m3outline)
readonly property color m3outlineVariant: root.layer(root.palette.m3outlineVariant)
readonly property color m3primary: root.layer(root.palette.m3primary)
readonly property color m3primaryContainer: root.layer(root.palette.m3primaryContainer)
readonly property color m3primaryFixed: root.layer(root.palette.m3primaryFixed)
readonly property color m3primaryFixedDim: root.layer(root.palette.m3primaryFixedDim)
readonly property color m3primary_paletteKeyColor: root.layer(root.palette.m3primary_paletteKeyColor)
readonly property color m3scrim: root.layer(root.palette.m3scrim)
readonly property color m3secondary: root.layer(root.palette.m3secondary)
readonly property color m3secondaryContainer: root.layer(root.palette.m3secondaryContainer)
readonly property color m3secondaryFixed: root.layer(root.palette.m3secondaryFixed)
readonly property color m3secondaryFixedDim: root.layer(root.palette.m3secondaryFixedDim)
readonly property color m3secondary_paletteKeyColor: root.layer(root.palette.m3secondary_paletteKeyColor)
readonly property color m3shadow: root.layer(root.palette.m3shadow)
readonly property color m3success: root.layer(root.palette.m3success)
readonly property color m3successContainer: root.layer(root.palette.m3successContainer)
readonly property color m3surface: root.layer(root.palette.m3surface, 0)
readonly property color m3surfaceBright: root.layer(root.palette.m3surfaceBright, 0)
readonly property color m3surfaceContainer: root.layer(root.palette.m3surfaceContainer)
readonly property color m3surfaceContainerHigh: root.layer(root.palette.m3surfaceContainerHigh)
readonly property color m3surfaceContainerHighest: root.layer(root.palette.m3surfaceContainerHighest)
readonly property color m3surfaceContainerLow: root.layer(root.palette.m3surfaceContainerLow)
readonly property color m3surfaceContainerLowest: root.layer(root.palette.m3surfaceContainerLowest)
readonly property color m3surfaceDim: root.layer(root.palette.m3surfaceDim, 0)
readonly property color m3surfaceTint: root.layer(root.palette.m3surfaceTint)
readonly property color m3surfaceVariant: root.layer(root.palette.m3surfaceVariant, 0)
readonly property color m3tertiary: root.layer(root.palette.m3tertiary)
readonly property color m3tertiaryContainer: root.layer(root.palette.m3tertiaryContainer)
readonly property color m3tertiaryFixed: root.layer(root.palette.m3tertiaryFixed)
readonly property color m3tertiaryFixedDim: root.layer(root.palette.m3tertiaryFixedDim)
readonly property color m3tertiary_paletteKeyColor: root.layer(root.palette.m3tertiary_paletteKeyColor)
}
component Transparency: QtObject {
readonly property real base: Appearance.transparency.base - (root.light ? 0.1 : 0)
readonly property bool enabled: Appearance.transparency.enabled
readonly property real layers: Appearance.transparency.layers
}
}
+19 -18
View File
@@ -2,28 +2,29 @@ import Quickshell.Io
import Quickshell
JsonObject {
property Apps apps: Apps {
}
property Color color: Color {
}
property Idle idle: Idle {
}
property string logo: ""
property string wallpaperPath: Quickshell.env("HOME") + "/Pictures/Wallpapers"
property Color color: Color {}
property Apps apps: Apps {}
property Idle idle: Idle {}
component Color: JsonObject {
property bool wallust: false
property bool schemeGeneration: true
property string mode: "dark"
property int scheduleDarkStart: 0
property int scheduleDarkEnd: 0
property bool neovimColors: false
component Apps: JsonObject {
property list<string> audio: ["pavucontrol"]
property list<string> explorer: ["dolphin"]
property list<string> playback: ["mpv"]
property list<string> terminal: ["kitty"]
}
component Color: JsonObject {
property string mode: "dark"
property bool neovimColors: false
property int scheduleDarkEnd: 0
property int scheduleDarkStart: 0
property bool schemeGeneration: true
property bool wallust: false
}
component Apps: JsonObject {
property list<string> terminal: ["kitty"]
property list<string> audio: ["pavucontrol"]
property list<string> playback: ["mpv"]
property list<string> explorer: ["dolphin"]
}
component Idle: JsonObject {
property list<var> timeouts: [
{
+69 -69
View File
@@ -1,28 +1,7 @@
import Quickshell.Io
JsonObject {
property int maxAppsShown: 10
property int maxWallpapers: 7
property string actionPrefix: ">"
property string specialPrefix: "@"
property Sizes sizes: Sizes {}
property UseFuzzy useFuzzy: UseFuzzy {}
component UseFuzzy: JsonObject {
property bool apps: false
property bool actions: false
property bool schemes: false
property bool variants: false
property bool wallpapers: false
}
component Sizes: JsonObject {
property int itemWidth: 600
property int itemHeight: 50
property int wallpaperWidth: 280
property int wallpaperHeight: 200
}
property list<var> actions: [
{
name: "Calculator",
@@ -32,53 +11,74 @@ JsonObject {
enabled: true,
dangerous: false
},
{
name: "Wallpaper",
icon: "image",
description: "Change the current wallpaper",
command: ["autocomplete", "wallpaper"],
enabled: true,
dangerous: false
},
{
name: "Shutdown",
icon: "power_settings_new",
description: "Shutdown the system",
command: ["systemctl", "poweroff"],
enabled: true,
dangerous: true
},
{
name: "Reboot",
icon: "cached",
description: "Reboot the system",
command: ["systemctl", "reboot"],
enabled: true,
dangerous: true
},
{
name: "Logout",
icon: "exit_to_app",
description: "Log out of the current session",
command: ["loginctl", "terminate-user", ""],
enabled: true,
dangerous: true
},
{
name: "Lock",
icon: "lock",
description: "Lock the current session",
command: ["loginctl", "lock-session"],
enabled: true,
dangerous: false
},
{
name: "Sleep",
icon: "bedtime",
description: "Suspend then hibernate",
command: ["systemctl", "suspend-then-hibernate"],
enabled: true,
dangerous: false
},
{
name: "Wallpaper",
icon: "image",
description: "Change the current wallpaper",
command: ["autocomplete", "wallpaper"],
enabled: true,
dangerous: false
},
{
name: "Shutdown",
icon: "power_settings_new",
description: "Shutdown the system",
command: ["systemctl", "poweroff"],
enabled: true,
dangerous: true
},
{
name: "Reboot",
icon: "cached",
description: "Reboot the system",
command: ["systemctl", "reboot"],
enabled: true,
dangerous: true
},
{
name: "Logout",
icon: "exit_to_app",
description: "Log out of the current session",
command: ["loginctl", "terminate-user", ""],
enabled: true,
dangerous: true
},
{
name: "Lock",
icon: "lock",
description: "Lock the current session",
command: ["loginctl", "lock-session"],
enabled: true,
dangerous: false
},
{
name: "Sleep",
icon: "bedtime",
description: "Suspend then hibernate",
command: ["systemctl", "suspend-then-hibernate"],
enabled: true,
dangerous: false
},
]
property int maxAppsShown: 10
property int maxWallpapers: 7
property Sizes sizes: Sizes {
}
property string specialPrefix: "@"
property UseFuzzy useFuzzy: UseFuzzy {
}
component Sizes: JsonObject {
property int itemHeight: 50
property int itemWidth: 600
property int wallpaperHeight: 200
property int wallpaperWidth: 280
}
component UseFuzzy: JsonObject {
property bool actions: false
property bool apps: false
property bool schemes: false
property bool variants: false
property bool wallpapers: false
}
}
+10 -9
View File
@@ -1,15 +1,16 @@
import Quickshell.Io
JsonObject {
property bool recolorLogo: false
property bool enableFprint: true
property int maxFprintTries: 3
property Sizes sizes: Sizes {}
property int blurAmount: 40
property bool enableFprint: true
property int maxFprintTries: 3
property bool recolorLogo: false
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property real heightMult: 0.7
property real ratio: 16 / 9
property int centerWidth: 600
}
component Sizes: JsonObject {
property int centerWidth: 600
property real heightMult: 0.7
property real ratio: 16 / 9
}
}
+20 -21
View File
@@ -2,26 +2,25 @@ pragma Singleton
import Quickshell
Singleton {
id: root
id: root
property real scale: Appearance.anim.durations.scale
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
readonly property int emphasizedAccelTime: 200 * scale
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
readonly property int emphasizedDecelTime: 400 * scale
readonly property int emphasizedTime: 500 * scale
readonly property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1.00, 1, 1]
readonly property int expressiveDefaultSpatialTime: 500 * scale
readonly property list<real> expressiveEffects: [0.34, 0.80, 0.34, 1.00, 1, 1]
readonly property int expressiveEffectsTime: 200 * scale
readonly property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.90, 1, 1]
readonly property int expressiveFastSpatialTime: 350 * scale
readonly property list<real> standard: [0.2, 0, 0, 1, 1, 1]
readonly property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
readonly property int standardAccelTime: 200 * scale
readonly property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
readonly property int standardDecelTime: 250 * scale
readonly property int standardTime: 300 * scale
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
readonly property int emphasizedAccelTime: 200 * scale
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
readonly property int emphasizedDecelTime: 400 * scale
readonly property int emphasizedTime: 500 * scale
readonly property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1.00, 1, 1]
readonly property int expressiveDefaultSpatialTime: 500 * scale
readonly property list<real> expressiveEffects: [0.34, 0.80, 0.34, 1.00, 1, 1]
readonly property int expressiveEffectsTime: 200 * scale
readonly property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.90, 1, 1]
readonly property int expressiveFastSpatialTime: 350 * scale
property real scale: Appearance.anim.durations.scale
readonly property list<real> standard: [0.2, 0, 0, 1, 1, 1]
readonly property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
readonly property int standardAccelTime: 200 * scale
readonly property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
readonly property int standardDecelTime: 250 * scale
readonly property int standardTime: 300 * scale
}
+14 -13
View File
@@ -1,18 +1,19 @@
import Quickshell.Io
JsonObject {
property bool expire: true
property int defaultExpireTimeout: 5000
property real clearThreshold: 0.3
property int expandThreshold: 20
property bool actionOnClick: false
property int groupPreviewNum: 3
property bool openExpanded: false
property Sizes sizes: Sizes {}
property bool actionOnClick: false
property real clearThreshold: 0.3
property int defaultExpireTimeout: 5000
property int expandThreshold: 20
property bool expire: true
property int groupPreviewNum: 3
property bool openExpanded: false
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int width: 400
property int image: 41
property int badge: 20
}
component Sizes: JsonObject {
property int badge: 20
property int image: 41
property int width: 400
}
}
+10 -9
View File
@@ -1,15 +1,16 @@
import Quickshell.Io
JsonObject {
property bool enabled: true
property int hideDelay: 3000
property bool enableBrightness: true
property bool enableMicrophone: true
property bool allMonBrightness: false
property Sizes sizes: Sizes {}
property bool enableBrightness: true
property bool enableMicrophone: true
property bool enabled: true
property int hideDelay: 3000
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int sliderWidth: 30
property int sliderHeight: 150
}
component Sizes: JsonObject {
property int sliderHeight: 150
property int sliderWidth: 30
}
}
+2 -2
View File
@@ -1,8 +1,8 @@
import Quickshell.Io
JsonObject {
property int rows: 2
property int columns: 5
property real scale: 0.16
property bool enable: false
property int rows: 2
property real scale: 0.16
}
+14 -14
View File
@@ -2,18 +2,18 @@ import Quickshell.Io
import QtQuick
JsonObject {
property string weatherLocation: ""
property bool useFahrenheit: false
property bool useTwelveHourClock: Qt.locale().timeFormat(Locale.ShortFormat).toLowerCase().includes("a")
property string gpuType: ""
property real audioIncrement: 0.1
property real brightnessIncrement: 0.1
property real maxVolume: 1.0
property string defaultPlayer: "Spotify"
property list<var> playerAliases: [
{
"from": "com.github.th_ch.youtube_music",
"to": "YT Music"
}
]
property real audioIncrement: 0.1
property real brightnessIncrement: 0.1
property string defaultPlayer: "Spotify"
property string gpuType: ""
property real maxVolume: 1.0
property list<var> playerAliases: [
{
"from": "com.github.th_ch.youtube_music",
"to": "YT Music"
}
]
property bool useFahrenheit: false
property bool useTwelveHourClock: Qt.locale().timeFormat(Locale.ShortFormat).toLowerCase().includes("a")
property string weatherLocation: ""
}
+6 -5
View File
@@ -1,10 +1,11 @@
import Quickshell.Io
JsonObject {
property bool enabled: true
property Sizes sizes: Sizes {}
property bool enabled: true
property Sizes sizes: Sizes {
}
component Sizes: JsonObject {
property int width: 430
}
component Sizes: JsonObject {
property int width: 430
}
}
+3 -3
View File
@@ -1,7 +1,7 @@
import Quickshell.Io
JsonObject {
property bool enabled: false
property real base: 0.85
property real layers: 0.4
property real base: 0.85
property bool enabled: false
property real layers: 0.4
}
+30 -30
View File
@@ -1,35 +1,35 @@
import Quickshell.Io
JsonObject {
property bool enabled: true
property int maxToasts: 4
property bool enabled: true
property int maxToasts: 4
property Sizes sizes: Sizes {
}
property Toasts toasts: Toasts {
}
property Vpn vpn: Vpn {
}
property Sizes sizes: Sizes {}
property Toasts toasts: Toasts {}
property Vpn vpn: Vpn {}
component Sizes: JsonObject {
property int width: 430
property int toastWidth: 430
}
component Toasts: JsonObject {
property bool configLoaded: true
property bool chargingChanged: true
property bool gameModeChanged: true
property bool dndChanged: true
property bool audioOutputChanged: true
property bool audioInputChanged: true
property bool capsLockChanged: true
property bool numLockChanged: true
property bool kbLayoutChanged: true
property bool kbLimit: true
property bool vpnChanged: true
property bool nowPlaying: false
}
component Vpn: JsonObject {
property bool enabled: false
property list<var> provider: ["netbird"]
}
component Sizes: JsonObject {
property int toastWidth: 430
property int width: 430
}
component Toasts: JsonObject {
property bool audioInputChanged: true
property bool audioOutputChanged: true
property bool capsLockChanged: true
property bool chargingChanged: true
property bool configLoaded: true
property bool dndChanged: true
property bool gameModeChanged: true
property bool kbLayoutChanged: true
property bool kbLimit: true
property bool nowPlaying: false
property bool numLockChanged: true
property bool vpnChanged: true
}
component Vpn: JsonObject {
property bool enabled: false
property list<var> provider: ["netbird"]
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
import Quickshell.Io
JsonObject {
property string textColor: "black"
property string inactiveTextColor: "white"
property string inactiveTextColor: "white"
property string textColor: "black"
}
+104 -111
View File
@@ -8,137 +8,130 @@ import Quickshell.Services.Pipewire
import QtQuick
Singleton {
id: root
id: root
property string previousSinkName: ""
property string previousSourceName: ""
readonly property bool muted: !!sink?.audio?.muted
readonly property var nodes: Pipewire.nodes.values.reduce((acc, node) => {
if (!node.isStream) {
if (node.isSink)
acc.sinks.push(node);
else if (node.audio)
acc.sources.push(node);
} else if (node.isStream && node.audio) {
// Application streams (output streams)
acc.streams.push(node);
}
return acc;
}, {
sources: [],
sinks: [],
streams: []
})
property string previousSinkName: ""
property string previousSourceName: ""
readonly property PwNode sink: Pipewire.defaultAudioSink
readonly property list<PwNode> sinks: nodes.sinks
readonly property PwNode source: Pipewire.defaultAudioSource
readonly property bool sourceMuted: !!source?.audio?.muted
readonly property real sourceVolume: source?.audio?.volume ?? 0
readonly property list<PwNode> sources: nodes.sources
readonly property list<PwNode> streams: nodes.streams
readonly property real volume: sink?.audio?.volume ?? 0
readonly property var nodes: Pipewire.nodes.values.reduce((acc, node) => {
if (!node.isStream) {
if (node.isSink)
acc.sinks.push(node);
else if (node.audio)
acc.sources.push(node);
} else if (node.isStream && node.audio) {
// Application streams (output streams)
acc.streams.push(node);
}
return acc;
}, {
sources: [],
sinks: [],
streams: []
})
function decrementSourceVolume(amount: real): void {
setSourceVolume(sourceVolume - (amount || Config.services.audioIncrement));
}
readonly property list<PwNode> sinks: nodes.sinks
readonly property list<PwNode> sources: nodes.sources
readonly property list<PwNode> streams: nodes.streams
function decrementVolume(amount: real): void {
setVolume(volume - (amount || Config.services.audioIncrement));
}
readonly property PwNode sink: Pipewire.defaultAudioSink
readonly property PwNode source: Pipewire.defaultAudioSource
function getStreamMuted(stream: PwNode): bool {
return !!stream?.audio?.muted;
}
readonly property bool muted: !!sink?.audio?.muted
readonly property real volume: sink?.audio?.volume ?? 0
function getStreamName(stream: PwNode): string {
if (!stream)
return qsTr("Unknown");
// Try application name first, then description, then name
return stream.applicationName || stream.description || stream.name || qsTr("Unknown Application");
}
readonly property bool sourceMuted: !!source?.audio?.muted
readonly property real sourceVolume: source?.audio?.volume ?? 0
function getStreamVolume(stream: PwNode): real {
return stream?.audio?.volume ?? 0;
}
function setVolume(newVolume: real): void {
if (sink?.ready && sink?.audio) {
sink.audio.muted = false;
sink.audio.volume = Math.max(0, Math.min(Config.services.maxVolume, newVolume));
}
}
function incrementSourceVolume(amount: real): void {
setSourceVolume(sourceVolume + (amount || Config.services.audioIncrement));
}
function incrementVolume(amount: real): void {
setVolume(volume + (amount || Config.services.audioIncrement));
}
function incrementVolume(amount: real): void {
setVolume(volume + (amount || Config.services.audioIncrement));
}
function decrementVolume(amount: real): void {
setVolume(volume - (amount || Config.services.audioIncrement));
}
function setAudioSink(newSink: PwNode): void {
Pipewire.preferredDefaultAudioSink = newSink;
}
function setSourceVolume(newVolume: real): void {
if (source?.ready && source?.audio) {
source.audio.muted = false;
source.audio.volume = Math.max(0, Math.min(Config.services.maxVolume, newVolume));
}
}
function setAudioSource(newSource: PwNode): void {
Pipewire.preferredDefaultAudioSource = newSource;
}
function incrementSourceVolume(amount: real): void {
setSourceVolume(sourceVolume + (amount || Config.services.audioIncrement));
}
function setSourceVolume(newVolume: real): void {
if (source?.ready && source?.audio) {
source.audio.muted = false;
source.audio.volume = Math.max(0, Math.min(Config.services.maxVolume, newVolume));
}
}
function decrementSourceVolume(amount: real): void {
setSourceVolume(sourceVolume - (amount || Config.services.audioIncrement));
}
function setStreamMuted(stream: PwNode, muted: bool): void {
if (stream?.ready && stream?.audio) {
stream.audio.muted = muted;
}
}
function setAudioSink(newSink: PwNode): void {
Pipewire.preferredDefaultAudioSink = newSink;
}
function setStreamVolume(stream: PwNode, newVolume: real): void {
if (stream?.ready && stream?.audio) {
stream.audio.muted = false;
stream.audio.volume = Math.max(0, Math.min(Config.services.maxVolume, newVolume));
}
}
function setAudioSource(newSource: PwNode): void {
Pipewire.preferredDefaultAudioSource = newSource;
}
function setVolume(newVolume: real): void {
if (sink?.ready && sink?.audio) {
sink.audio.muted = false;
sink.audio.volume = Math.max(0, Math.min(Config.services.maxVolume, newVolume));
}
}
function setStreamVolume(stream: PwNode, newVolume: real): void {
if (stream?.ready && stream?.audio) {
stream.audio.muted = false;
stream.audio.volume = Math.max(0, Math.min(Config.services.maxVolume, newVolume));
}
}
Component.onCompleted: {
previousSinkName = sink?.description || sink?.name || qsTr("Unknown Device");
previousSourceName = source?.description || source?.name || qsTr("Unknown Device");
}
onSinkChanged: {
if (!sink?.ready)
return;
function setStreamMuted(stream: PwNode, muted: bool): void {
if (stream?.ready && stream?.audio) {
stream.audio.muted = muted;
}
}
const newSinkName = sink.description || sink.name || qsTr("Unknown Device");
function getStreamVolume(stream: PwNode): real {
return stream?.audio?.volume ?? 0;
}
if (previousSinkName && previousSinkName !== newSinkName && Config.utilities.toasts.audioOutputChanged)
Toaster.toast(qsTr("Audio output changed"), qsTr("Now using: %1").arg(newSinkName), "volume_up");
function getStreamMuted(stream: PwNode): bool {
return !!stream?.audio?.muted;
}
previousSinkName = newSinkName;
}
onSourceChanged: {
if (!source?.ready)
return;
function getStreamName(stream: PwNode): string {
if (!stream)
return qsTr("Unknown");
// Try application name first, then description, then name
return stream.applicationName || stream.description || stream.name || qsTr("Unknown Application");
}
const newSourceName = source.description || source.name || qsTr("Unknown Device");
onSinkChanged: {
if (!sink?.ready)
return;
if (previousSourceName && previousSourceName !== newSourceName && Config.utilities.toasts.audioInputChanged)
Toaster.toast(qsTr("Audio input changed"), qsTr("Now using: %1").arg(newSourceName), "mic");
const newSinkName = sink.description || sink.name || qsTr("Unknown Device");
previousSourceName = newSourceName;
}
if (previousSinkName && previousSinkName !== newSinkName && Config.utilities.toasts.audioOutputChanged)
Toaster.toast(qsTr("Audio output changed"), qsTr("Now using: %1").arg(newSinkName), "volume_up");
previousSinkName = newSinkName;
}
onSourceChanged: {
if (!source?.ready)
return;
const newSourceName = source.description || source.name || qsTr("Unknown Device");
if (previousSourceName && previousSourceName !== newSourceName && Config.utilities.toasts.audioInputChanged)
Toaster.toast(qsTr("Audio input changed"), qsTr("Now using: %1").arg(newSourceName), "mic");
previousSourceName = newSourceName;
}
Component.onCompleted: {
previousSinkName = sink?.description || sink?.name || qsTr("Unknown Device");
previousSourceName = source?.description || source?.name || qsTr("Unknown Device");
}
PwObjectTracker {
objects: [...root.sinks, ...root.sources, ...root.streams]
}
PwObjectTracker {
objects: [...root.sinks, ...root.sources, ...root.streams]
}
}
+285 -282
View File
@@ -5,319 +5,322 @@ import Quickshell.Io
import QtQuick
Singleton {
id: root
id: root
Component.onCompleted: {
// Trigger ethernet device detection after initialization
Qt.callLater(() => {
getEthernetDevices();
});
// Load saved connections on startup
Nmcli.loadSavedConnections(() => {
root.savedConnections = Nmcli.savedConnections;
root.savedConnectionSsids = Nmcli.savedConnectionSsids;
});
// Get initial WiFi status
Nmcli.getWifiStatus(enabled => {
root.wifiEnabled = enabled;
});
// Sync networks from Nmcli on startup
Qt.callLater(() => {
syncNetworksFromNmcli();
}, 100);
}
readonly property AccessPoint active: networks.find(n => n.active) ?? null
readonly property var activeEthernet: ethernetDevices.find(d => d.connected) ?? null
property int ethernetDeviceCount: 0
property var ethernetDeviceDetails: null
property list<var> ethernetDevices: []
property bool ethernetProcessRunning: false
readonly property list<AccessPoint> networks: []
property var pendingConnection: null
property list<string> savedConnectionSsids: []
property list<string> savedConnections: []
readonly property bool scanning: Nmcli.scanning
property bool wifiEnabled: true
property var wirelessDeviceDetails: null
readonly property list<AccessPoint> networks: []
readonly property AccessPoint active: networks.find(n => n.active) ?? null
property bool wifiEnabled: true
readonly property bool scanning: Nmcli.scanning
signal connectionFailed(string ssid)
property list<var> ethernetDevices: []
readonly property var activeEthernet: ethernetDevices.find(d => d.connected) ?? null
property int ethernetDeviceCount: 0
property bool ethernetProcessRunning: false
property var ethernetDeviceDetails: null
property var wirelessDeviceDetails: null
function cidrToSubnetMask(cidr: string): string {
// Convert CIDR notation (e.g., "24") to subnet mask (e.g., "255.255.255.0")
const cidrNum = parseInt(cidr);
if (isNaN(cidrNum) || cidrNum < 0 || cidrNum > 32) {
return "";
}
function enableWifi(enabled: bool): void {
Nmcli.enableWifi(enabled, result => {
if (result.success) {
root.getWifiStatus();
Nmcli.getNetworks(() => {
syncNetworksFromNmcli();
});
}
});
}
const mask = (0xffffffff << (32 - cidrNum)) >>> 0;
const octets = [(mask >>> 24) & 0xff, (mask >>> 16) & 0xff, (mask >>> 8) & 0xff, mask & 0xff];
function toggleWifi(): void {
Nmcli.toggleWifi(result => {
if (result.success) {
root.getWifiStatus();
Nmcli.getNetworks(() => {
syncNetworksFromNmcli();
});
}
});
}
return octets.join(".");
}
function rescanWifi(): void {
Nmcli.rescanWifi();
}
function connectEthernet(connectionName: string, interfaceName: string): void {
Nmcli.connectEthernet(connectionName, interfaceName, result => {
if (result.success) {
getEthernetDevices();
// Refresh device details after connection
Qt.callLater(() => {
const activeDevice = root.ethernetDevices.find(function (d) {
return d.connected;
});
if (activeDevice && activeDevice.interface) {
updateEthernetDeviceDetails(activeDevice.interface);
}
}, 1000);
}
});
}
property var pendingConnection: null
signal connectionFailed(string ssid)
function connectToNetwork(ssid: string, password: string, bssid: string, callback: var): void {
// Set up pending connection tracking if callback provided
if (callback) {
const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0;
root.pendingConnection = {
ssid: ssid,
bssid: hasBssid ? bssid : "",
callback: callback
};
}
function connectToNetwork(ssid: string, password: string, bssid: string, callback: var): void {
// Set up pending connection tracking if callback provided
if (callback) {
const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0;
root.pendingConnection = {
ssid: ssid,
bssid: hasBssid ? bssid : "",
callback: callback
};
}
Nmcli.connectToNetwork(ssid, password, bssid, result => {
if (result && result.success) {
// Connection successful
if (callback)
callback(result);
root.pendingConnection = null;
} else if (result && result.needsPassword) {
// Password needed - callback will handle showing dialog
if (callback)
callback(result);
} else {
// Connection failed
if (result && result.error) {
root.connectionFailed(ssid);
}
if (callback)
callback(result);
root.pendingConnection = null;
}
});
}
Nmcli.connectToNetwork(ssid, password, bssid, result => {
if (result && result.success) {
// Connection successful
if (callback)
callback(result);
root.pendingConnection = null;
} else if (result && result.needsPassword) {
// Password needed - callback will handle showing dialog
if (callback)
callback(result);
} else {
// Connection failed
if (result && result.error) {
root.connectionFailed(ssid);
}
if (callback)
callback(result);
root.pendingConnection = null;
}
});
}
function connectToNetworkWithPasswordCheck(ssid: string, isSecure: bool, callback: var, bssid: string): void {
// Set up pending connection tracking
const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0;
root.pendingConnection = {
ssid: ssid,
bssid: hasBssid ? bssid : "",
callback: callback
};
function connectToNetworkWithPasswordCheck(ssid: string, isSecure: bool, callback: var, bssid: string): void {
// Set up pending connection tracking
const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0;
root.pendingConnection = {
ssid: ssid,
bssid: hasBssid ? bssid : "",
callback: callback
};
Nmcli.connectToNetworkWithPasswordCheck(ssid, isSecure, result => {
if (result && result.success) {
// Connection successful
if (callback)
callback(result);
root.pendingConnection = null;
} else if (result && result.needsPassword) {
// Password needed - callback will handle showing dialog
if (callback)
callback(result);
} else {
// Connection failed
if (result && result.error) {
root.connectionFailed(ssid);
}
if (callback)
callback(result);
root.pendingConnection = null;
}
}, bssid);
}
Nmcli.connectToNetworkWithPasswordCheck(ssid, isSecure, result => {
if (result && result.success) {
// Connection successful
if (callback)
callback(result);
root.pendingConnection = null;
} else if (result && result.needsPassword) {
// Password needed - callback will handle showing dialog
if (callback)
callback(result);
} else {
// Connection failed
if (result && result.error) {
root.connectionFailed(ssid);
}
if (callback)
callback(result);
root.pendingConnection = null;
}
}, bssid);
}
function disconnectEthernet(connectionName: string): void {
Nmcli.disconnectEthernet(connectionName, result => {
if (result.success) {
getEthernetDevices();
// Clear device details after disconnection
Qt.callLater(() => {
root.ethernetDeviceDetails = null;
});
}
});
}
function disconnectFromNetwork(): void {
// Try to disconnect - use connection name if available, otherwise use device
Nmcli.disconnectFromNetwork();
// Refresh network list after disconnection
Qt.callLater(() => {
Nmcli.getNetworks(() => {
syncNetworksFromNmcli();
});
}, 500);
}
function disconnectFromNetwork(): void {
// Try to disconnect - use connection name if available, otherwise use device
Nmcli.disconnectFromNetwork();
// Refresh network list after disconnection
Qt.callLater(() => {
Nmcli.getNetworks(() => {
syncNetworksFromNmcli();
});
}, 500);
}
function forgetNetwork(ssid: string): void {
// Delete the connection profile for this network
// This will remove the saved password and connection settings
Nmcli.forgetNetwork(ssid, result => {
if (result.success) {
// Refresh network list after deletion
Qt.callLater(() => {
Nmcli.getNetworks(() => {
syncNetworksFromNmcli();
});
}, 500);
}
});
}
function enableWifi(enabled: bool): void {
Nmcli.enableWifi(enabled, result => {
if (result.success) {
root.getWifiStatus();
Nmcli.getNetworks(() => {
syncNetworksFromNmcli();
});
}
});
}
property list<string> savedConnections: []
property list<string> savedConnectionSsids: []
function forgetNetwork(ssid: string): void {
// Delete the connection profile for this network
// This will remove the saved password and connection settings
Nmcli.forgetNetwork(ssid, result => {
if (result.success) {
// Refresh network list after deletion
Qt.callLater(() => {
Nmcli.getNetworks(() => {
syncNetworksFromNmcli();
});
}, 500);
}
});
}
// Sync saved connections from Nmcli when they're updated
Connections {
target: Nmcli
function onSavedConnectionsChanged() {
root.savedConnections = Nmcli.savedConnections;
}
function onSavedConnectionSsidsChanged() {
root.savedConnectionSsids = Nmcli.savedConnectionSsids;
}
}
function getEthernetDevices(): void {
root.ethernetProcessRunning = true;
Nmcli.getEthernetInterfaces(interfaces => {
root.ethernetDevices = Nmcli.ethernetDevices;
root.ethernetDeviceCount = Nmcli.ethernetDevices.length;
root.ethernetProcessRunning = false;
});
}
function syncNetworksFromNmcli(): void {
const rNetworks = root.networks;
const nNetworks = Nmcli.networks;
function getWifiStatus(): void {
Nmcli.getWifiStatus(enabled => {
root.wifiEnabled = enabled;
});
}
// Build a map of existing networks by key
const existingMap = new Map();
for (const rn of rNetworks) {
const key = `${rn.frequency}:${rn.ssid}:${rn.bssid}`;
existingMap.set(key, rn);
}
function hasSavedProfile(ssid: string): bool {
// Use Nmcli's hasSavedProfile which has the same logic
return Nmcli.hasSavedProfile(ssid);
}
// Build a map of new networks by key
const newMap = new Map();
for (const nn of nNetworks) {
const key = `${nn.frequency}:${nn.ssid}:${nn.bssid}`;
newMap.set(key, nn);
}
function rescanWifi(): void {
Nmcli.rescanWifi();
}
// Remove networks that no longer exist
for (const [key, network] of existingMap) {
if (!newMap.has(key)) {
const index = rNetworks.indexOf(network);
if (index >= 0) {
rNetworks.splice(index, 1);
network.destroy();
}
}
}
function syncNetworksFromNmcli(): void {
const rNetworks = root.networks;
const nNetworks = Nmcli.networks;
// Add or update networks from Nmcli
for (const [key, nNetwork] of newMap) {
const existing = existingMap.get(key);
if (existing) {
// Update existing network's lastIpcObject
existing.lastIpcObject = nNetwork.lastIpcObject;
} else {
// Create new AccessPoint from Nmcli's data
rNetworks.push(apComp.createObject(root, {
lastIpcObject: nNetwork.lastIpcObject
}));
}
}
}
// Build a map of existing networks by key
const existingMap = new Map();
for (const rn of rNetworks) {
const key = `${rn.frequency}:${rn.ssid}:${rn.bssid}`;
existingMap.set(key, rn);
}
component AccessPoint: QtObject {
required property var lastIpcObject
readonly property string ssid: lastIpcObject.ssid
readonly property string bssid: lastIpcObject.bssid
readonly property int strength: lastIpcObject.strength
readonly property int frequency: lastIpcObject.frequency
readonly property bool active: lastIpcObject.active
readonly property string security: lastIpcObject.security
readonly property bool isSecure: security.length > 0
}
// Build a map of new networks by key
const newMap = new Map();
for (const nn of nNetworks) {
const key = `${nn.frequency}:${nn.ssid}:${nn.bssid}`;
newMap.set(key, nn);
}
Component {
id: apComp
AccessPoint {}
}
// Remove networks that no longer exist
for (const [key, network] of existingMap) {
if (!newMap.has(key)) {
const index = rNetworks.indexOf(network);
if (index >= 0) {
rNetworks.splice(index, 1);
network.destroy();
}
}
}
function hasSavedProfile(ssid: string): bool {
// Use Nmcli's hasSavedProfile which has the same logic
return Nmcli.hasSavedProfile(ssid);
}
// Add or update networks from Nmcli
for (const [key, nNetwork] of newMap) {
const existing = existingMap.get(key);
if (existing) {
// Update existing network's lastIpcObject
existing.lastIpcObject = nNetwork.lastIpcObject;
} else {
// Create new AccessPoint from Nmcli's data
rNetworks.push(apComp.createObject(root, {
lastIpcObject: nNetwork.lastIpcObject
}));
}
}
}
function getWifiStatus(): void {
Nmcli.getWifiStatus(enabled => {
root.wifiEnabled = enabled;
});
}
function toggleWifi(): void {
Nmcli.toggleWifi(result => {
if (result.success) {
root.getWifiStatus();
Nmcli.getNetworks(() => {
syncNetworksFromNmcli();
});
}
});
}
function getEthernetDevices(): void {
root.ethernetProcessRunning = true;
Nmcli.getEthernetInterfaces(interfaces => {
root.ethernetDevices = Nmcli.ethernetDevices;
root.ethernetDeviceCount = Nmcli.ethernetDevices.length;
root.ethernetProcessRunning = false;
});
}
function updateEthernetDeviceDetails(interfaceName: string): void {
Nmcli.getEthernetDeviceDetails(interfaceName, details => {
root.ethernetDeviceDetails = details;
});
}
function connectEthernet(connectionName: string, interfaceName: string): void {
Nmcli.connectEthernet(connectionName, interfaceName, result => {
if (result.success) {
getEthernetDevices();
// Refresh device details after connection
Qt.callLater(() => {
const activeDevice = root.ethernetDevices.find(function (d) {
return d.connected;
});
if (activeDevice && activeDevice.interface) {
updateEthernetDeviceDetails(activeDevice.interface);
}
}, 1000);
}
});
}
function updateWirelessDeviceDetails(): void {
// Find the wireless interface by looking for wifi devices
// Pass empty string to let Nmcli find the active interface automatically
Nmcli.getWirelessDeviceDetails("", details => {
root.wirelessDeviceDetails = details;
});
}
function disconnectEthernet(connectionName: string): void {
Nmcli.disconnectEthernet(connectionName, result => {
if (result.success) {
getEthernetDevices();
// Clear device details after disconnection
Qt.callLater(() => {
root.ethernetDeviceDetails = null;
});
}
});
}
Component.onCompleted: {
// Trigger ethernet device detection after initialization
Qt.callLater(() => {
getEthernetDevices();
});
// Load saved connections on startup
Nmcli.loadSavedConnections(() => {
root.savedConnections = Nmcli.savedConnections;
root.savedConnectionSsids = Nmcli.savedConnectionSsids;
});
// Get initial WiFi status
Nmcli.getWifiStatus(enabled => {
root.wifiEnabled = enabled;
});
// Sync networks from Nmcli on startup
Qt.callLater(() => {
syncNetworksFromNmcli();
}, 100);
}
function updateEthernetDeviceDetails(interfaceName: string): void {
Nmcli.getEthernetDeviceDetails(interfaceName, details => {
root.ethernetDeviceDetails = details;
});
}
// Sync saved connections from Nmcli when they're updated
Connections {
function onSavedConnectionSsidsChanged() {
root.savedConnectionSsids = Nmcli.savedConnectionSsids;
}
function updateWirelessDeviceDetails(): void {
// Find the wireless interface by looking for wifi devices
// Pass empty string to let Nmcli find the active interface automatically
Nmcli.getWirelessDeviceDetails("", details => {
root.wirelessDeviceDetails = details;
});
}
function onSavedConnectionsChanged() {
root.savedConnections = Nmcli.savedConnections;
}
function cidrToSubnetMask(cidr: string): string {
// Convert CIDR notation (e.g., "24") to subnet mask (e.g., "255.255.255.0")
const cidrNum = parseInt(cidr);
if (isNaN(cidrNum) || cidrNum < 0 || cidrNum > 32) {
return "";
}
target: Nmcli
}
const mask = (0xffffffff << (32 - cidrNum)) >>> 0;
const octets = [(mask >>> 24) & 0xff, (mask >>> 16) & 0xff, (mask >>> 8) & 0xff, mask & 0xff];
Component {
id: apComp
return octets.join(".");
}
AccessPoint {
}
}
Process {
running: true
command: ["nmcli", "m"]
stdout: SplitParser {
onRead: {
Nmcli.getNetworks(() => {
syncNetworksFromNmcli();
});
getEthernetDevices();
}
}
}
Process {
command: ["nmcli", "m"]
running: true
stdout: SplitParser {
onRead: {
Nmcli.getNetworks(() => {
syncNetworksFromNmcli();
});
getEthernetDevices();
}
}
}
component AccessPoint: QtObject {
readonly property bool active: lastIpcObject.active
readonly property string bssid: lastIpcObject.bssid
readonly property int frequency: lastIpcObject.frequency
readonly property bool isSecure: security.length > 0
required property var lastIpcObject
readonly property string security: lastIpcObject.security
readonly property string ssid: lastIpcObject.ssid
readonly property int strength: lastIpcObject.strength
}
}
+1349 -1343
View File
File diff suppressed because it is too large Load Diff
+280 -279
View File
@@ -14,335 +14,336 @@ import qs.Paths
import qs.Config
Singleton {
id: root
id: root
property list<Notif> list: []
readonly property list<Notif> notClosed: list.filter( n => !n.closed )
readonly property list<Notif> popups: list.filter( n => n.popup )
property alias dnd: props.dnd
property alias server: server
property alias dnd: props.dnd
property list<Notif> list: []
property bool loaded
readonly property list<Notif> notClosed: list.filter(n => !n.closed)
readonly property list<Notif> popups: list.filter(n => n.popup)
property alias server: server
property bool loaded
onListChanged: {
if (loaded) {
saveTimer.restart();
}
if (root.list.length > 0) {
HasNotifications.hasNotifications = true;
} else {
HasNotifications.hasNotifications = false;
}
}
onListChanged: {
if ( loaded ) {
saveTimer.restart();
}
if ( root.list.length > 0 ) {
HasNotifications.hasNotifications = true;
} else {
HasNotifications.hasNotifications = false;
}
}
Timer {
id: saveTimer
Timer {
id: saveTimer
interval: 1000
onTriggered: storage.setText( JSON.stringify( root.notClosed.map( n => ({
time: n.time,
id: n.id,
summary: n.summary,
body: n.body,
appIcon: n.appIcon,
appName: n.appName,
image: n.image,
expireTimeout: n.expireTimeout,
urgency: n.urgency,
resident: n.resident,
hasActionIcons: n.hasActionIcons,
actions: n.actions
}))));
}
interval: 1000
PersistentProperties {
id: props
onTriggered: storage.setText(JSON.stringify(root.notClosed.map(n => ({
time: n.time,
id: n.id,
summary: n.summary,
body: n.body,
appIcon: n.appIcon,
appName: n.appName,
image: n.image,
expireTimeout: n.expireTimeout,
urgency: n.urgency,
resident: n.resident,
hasActionIcons: n.hasActionIcons,
actions: n.actions
}))))
}
property bool dnd
PersistentProperties {
id: props
reloadableId: "notifs"
}
property bool dnd
NotificationServer {
id: server
reloadableId: "notifs"
}
keepOnReload: false
actionsSupported: true
bodyHyperlinksSupported: true
bodyImagesSupported: true
bodyMarkupSupported: true
imageSupported: true
persistenceSupported: true
NotificationServer {
id: server
onNotification: notif => {
notif.tracked = true;
actionsSupported: true
bodyHyperlinksSupported: true
bodyImagesSupported: true
bodyMarkupSupported: true
imageSupported: true
keepOnReload: false
persistenceSupported: true
const comp = notifComp.createObject(root, {
popup: !props.dnd,
notification: notif
});
root.list = [comp, ...root.list];
}
}
onNotification: notif => {
notif.tracked = true;
FileView {
id: storage
path: `${Paths.state}/notifs.json`
const comp = notifComp.createObject(root, {
popup: !props.dnd,
notification: notif
});
root.list = [comp, ...root.list];
}
}
onLoaded: {
const data = JSON.parse(text());
for (const notif of data)
root.list.push(notifComp.createObject(root, notif));
root.list.sort((a, b) => b.time - a.time);
root.loaded = true;
}
FileView {
id: storage
onLoadFailed: err => {
if (err === FileViewError.FileNotFound) {
root.loaded = true;
setText("[]");
}
}
}
path: `${Paths.state}/notifs.json`
CustomShortcut {
name: "clearnotifs"
description: "Clear all notifications"
onPressed: {
for (const notif of root.list.slice())
notif.close();
}
}
onLoadFailed: err => {
if (err === FileViewError.FileNotFound) {
root.loaded = true;
setText("[]");
}
}
onLoaded: {
const data = JSON.parse(text());
for (const notif of data)
root.list.push(notifComp.createObject(root, notif));
root.list.sort((a, b) => b.time - a.time);
root.loaded = true;
}
}
IpcHandler {
target: "notifs"
CustomShortcut {
description: "Clear all notifications"
name: "clearnotifs"
function clear(): void {
for (const notif of root.list.slice())
notif.close();
}
onPressed: {
for (const notif of root.list.slice())
notif.close();
}
}
function isDndEnabled(): bool {
return props.dnd;
}
IpcHandler {
function clear(): void {
for (const notif of root.list.slice())
notif.close();
}
function toggleDnd(): void {
props.dnd = !props.dnd;
}
function disableDnd(): void {
props.dnd = false;
}
function enableDnd(): void {
props.dnd = true;
}
function enableDnd(): void {
props.dnd = true;
}
function disableDnd(): void {
props.dnd = false;
}
}
function isDndEnabled(): bool {
return props.dnd;
}
component Notif: QtObject {
id: notif
function toggleDnd(): void {
props.dnd = !props.dnd;
}
property bool popup
property bool closed
property var locks: new Set()
target: "notifs"
}
property date time: new Date()
readonly property string timeStr: {
const diff = Time.date.getTime() - time.getTime();
const m = Math.floor(diff / 60000);
Component {
id: notifComp
if (m < 1)
return qsTr("now");
Notif {
}
}
const h = Math.floor(m / 60);
const d = Math.floor(h / 24);
component Notif: QtObject {
id: notif
if (d > 0)
return `${d}d`;
if (h > 0)
return `${h}h`;
return `${m}m`;
}
property list<var> actions
property string appIcon
property string appName
property string body
property bool closed
readonly property Connections conn: Connections {
function onActionsChanged(): void {
notif.actions = notif.notification.actions.map(a => ({
identifier: a.identifier,
text: a.text,
invoke: () => a.invoke()
}));
}
property Notification notification
property string id
property string summary
property string body
property string appIcon
property string appName
property string image
property real expireTimeout: 5
property int urgency: NotificationUrgency.Normal
property bool resident
property bool hasActionIcons
property list<var> actions
function onAppIconChanged(): void {
notif.appIcon = notif.notification.appIcon;
}
readonly property Timer timer: Timer {
property int totalTime: Config.notifs.defaultExpireTimeout
property int remainingTime: totalTime
property bool paused: false
function onAppNameChanged(): void {
notif.appName = notif.notification.appName;
}
running: !paused
repeat: true
interval: 50
onTriggered: {
remainingTime -= interval;
function onBodyChanged(): void {
notif.body = notif.notification.body;
}
if ( remainingTime <= 0 ) {
remainingTime = 0;
notif.popup = false;
stop();
function onClosed(): void {
notif.close();
}
function onExpireTimeoutChanged(): void {
notif.expireTimeout = notif.notification.expireTimeout;
}
function onHasActionIconsChanged(): void {
notif.hasActionIcons = notif.notification.hasActionIcons;
}
function onImageChanged(): void {
notif.image = notif.notification.image;
if (notif.notification?.image)
notif.dummyImageLoader.active = true;
}
function onResidentChanged(): void {
notif.resident = notif.notification.resident;
}
function onSummaryChanged(): void {
notif.summary = notif.notification.summary;
}
function onUrgencyChanged(): void {
notif.urgency = notif.notification.urgency;
}
target: notif.notification
}
readonly property LazyLoader dummyImageLoader: LazyLoader {
active: false
PanelWindow {
color: "transparent"
implicitHeight: Config.notifs.sizes.image
implicitWidth: Config.notifs.sizes.image
mask: Region {
}
}
}
readonly property LazyLoader dummyImageLoader: LazyLoader {
active: false
Image {
function tryCache(): void {
if (status !== Image.Ready || width != Config.notifs.sizes.image || height != Config.notifs.sizes.image)
return;
PanelWindow {
implicitWidth: Config.notifs.sizes.image
implicitHeight: Config.notifs.sizes.image
color: "transparent"
mask: Region {}
const cacheKey = notif.appName + notif.summary + notif.id;
let h1 = 0xdeadbeef, h2 = 0x41c6ce57, ch;
for (let i = 0; i < cacheKey.length; i++) {
ch = cacheKey.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
const hash = (h2 >>> 0).toString(16).padStart(8, 0) + (h1 >>> 0).toString(16).padStart(8, 0);
Image {
function tryCache(): void {
if (status !== Image.Ready || width != Config.notifs.sizes.image || height != Config.notifs.sizes.image)
return;
const cacheKey = notif.appName + notif.summary + notif.id;
let h1 = 0xdeadbeef, h2 = 0x41c6ce57, ch;
for (let i = 0; i < cacheKey.length; i++) {
ch = cacheKey.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
const hash = (h2 >>> 0).toString(16).padStart(8, 0) + (h1 >>> 0).toString(16).padStart(8, 0);
const cache = `${Paths.notifimagecache}/${hash}.png`;
const cache = `${Paths.notifimagecache}/${hash}.png`;
ZShellIo.saveItem(this, Qt.resolvedUrl(cache), () => {
notif.image = cache;
notif.dummyImageLoader.active = false;
});
}
}
anchors.fill: parent
source: Qt.resolvedUrl(notif.image)
fillMode: Image.PreserveAspectCrop
cache: false
asynchronous: true
opacity: 0
anchors.fill: parent
asynchronous: true
cache: false
fillMode: Image.PreserveAspectCrop
opacity: 0
source: Qt.resolvedUrl(notif.image)
onStatusChanged: tryCache()
onWidthChanged: tryCache()
onHeightChanged: tryCache()
}
}
}
onHeightChanged: tryCache()
onStatusChanged: tryCache()
onWidthChanged: tryCache()
}
}
}
property real expireTimeout: 5
property bool hasActionIcons
property string id
property string image
property var locks: new Set()
property Notification notification
property bool popup
property bool resident
property string summary
property date time: new Date()
readonly property string timeStr: {
const diff = Time.date.getTime() - time.getTime();
const m = Math.floor(diff / 60000);
readonly property Connections conn: Connections {
target: notif.notification
if (m < 1)
return qsTr("now");
function onClosed(): void {
notif.close();
}
const h = Math.floor(m / 60);
const d = Math.floor(h / 24);
function onSummaryChanged(): void {
notif.summary = notif.notification.summary;
}
if (d > 0)
return `${d}d`;
if (h > 0)
return `${h}h`;
return `${m}m`;
}
readonly property Timer timer: Timer {
property bool paused: false
property int remainingTime: totalTime
property int totalTime: Config.notifs.defaultExpireTimeout
function onBodyChanged(): void {
notif.body = notif.notification.body;
}
interval: 50
repeat: true
running: !paused
function onAppIconChanged(): void {
notif.appIcon = notif.notification.appIcon;
}
onTriggered: {
remainingTime -= interval;
function onAppNameChanged(): void {
notif.appName = notif.notification.appName;
}
if (remainingTime <= 0) {
remainingTime = 0;
notif.popup = false;
stop();
}
}
}
property int urgency: NotificationUrgency.Normal
function onImageChanged(): void {
notif.image = notif.notification.image;
if (notif.notification?.image)
notif.dummyImageLoader.active = true;
}
function close(): void {
closed = true;
if (locks.size === 0 && root.list.includes(this)) {
root.list = root.list.filter(n => n !== this);
notification?.dismiss();
destroy();
}
}
function onExpireTimeoutChanged(): void {
notif.expireTimeout = notif.notification.expireTimeout;
}
function lock(item: Item): void {
locks.add(item);
}
function onUrgencyChanged(): void {
notif.urgency = notif.notification.urgency;
}
function unlock(item: Item): void {
locks.delete(item);
if (closed)
close();
}
function onResidentChanged(): void {
notif.resident = notif.notification.resident;
}
Component.onCompleted: {
if (!notification)
return;
function onHasActionIconsChanged(): void {
notif.hasActionIcons = notif.notification.hasActionIcons;
}
function onActionsChanged(): void {
notif.actions = notif.notification.actions.map(a => ({
identifier: a.identifier,
text: a.text,
invoke: () => a.invoke()
}));
}
}
function lock(item: Item): void {
locks.add(item);
}
function unlock(item: Item): void {
locks.delete(item);
if (closed)
close();
}
function close(): void {
closed = true;
if (locks.size === 0 && root.list.includes(this)) {
root.list = root.list.filter(n => n !== this);
notification?.dismiss();
destroy();
}
}
Component.onCompleted: {
if (!notification)
return;
id = notification.id;
summary = notification.summary;
body = notification.body;
appIcon = notification.appIcon;
appName = notification.appName;
image = notification.image;
if (notification?.image)
dummyImageLoader.active = true;
expireTimeout = notification.expireTimeout;
urgency = notification.urgency;
resident = notification.resident;
hasActionIcons = notification.hasActionIcons;
actions = notification.actions.map(a => ({
identifier: a.identifier,
text: a.text,
invoke: () => a.invoke()
}));
}
}
Component {
id: notifComp
Notif {}
}
id = notification.id;
summary = notification.summary;
body = notification.body;
appIcon = notification.appIcon;
appName = notification.appName;
image = notification.image;
if (notification?.image)
dummyImageLoader.active = true;
expireTimeout = notification.expireTimeout;
urgency = notification.urgency;
resident = notification.resident;
hasActionIcons = notification.hasActionIcons;
actions = notification.actions.map(a => ({
identifier: a.identifier,
text: a.text,
invoke: () => a.invoke()
}));
}
}
}
+23 -31
View File
@@ -11,80 +11,72 @@ import qs.Modules.Launcher as Launcher
import qs.Modules.Settings as Settings
Shape {
id: root
id: root
required property Panels panels
required property Item bar
required property Item bar
required property Panels panels
required property PersistentProperties visibilities
anchors.fill: parent
// anchors.margins: 8
anchors.topMargin: bar.implicitHeight
preferredRendererType: Shape.CurveRenderer
anchors.fill: parent
// anchors.margins: 8
anchors.topMargin: bar.implicitHeight
preferredRendererType: Shape.CurveRenderer
Component.onCompleted: console.log(root.bar.implicitHeight, root.bar.anchors.topMargin)
Osd.Background {
wrapper: root.panels.osd
startX: root.width - root.panels.sidebar.width
startY: ( root.height - wrapper.height ) / 2 - rounding
startY: (root.height - wrapper.height) / 2 - rounding
wrapper: root.panels.osd
}
Modules.Background {
wrapper: root.panels.popouts
invertBottomRounding: wrapper.x <= 0
startX: wrapper.x - 8
startY: wrapper.y
}
Modules.Background {
invertBottomRounding: wrapper.x <= 0
startX: wrapper.x - 8
startY: wrapper.y
wrapper: root.panels.popouts
}
Notifications.Background {
wrapper: root.panels.notifications
sidebar: sidebar
startX: root.width
startY: 0
wrapper: root.panels.notifications
}
Launcher.Background {
wrapper: root.panels.launcher
startX: ( root.width - wrapper.width ) / 2 - rounding
startX: (root.width - wrapper.width) / 2 - rounding
startY: root.height
wrapper: root.panels.launcher
}
Dashboard.Background {
wrapper: root.panels.dashboard
startX: root.width - root.panels.dashboard.width - rounding
startY: 0
wrapper: root.panels.dashboard
}
Utils.Background {
wrapper: root.panels.utilities
sidebar: sidebar
startX: root.width
startY: root.height
wrapper: root.panels.utilities
}
Sidebar.Background {
id: sidebar
wrapper: root.panels.sidebar
panels: root.panels
startX: root.width
startY: root.panels.notifications.height
wrapper: root.panels.sidebar
}
Settings.Background {
id: settings
wrapper: root.panels.settings
startX: ( root.width - wrapper.width ) / 2 - rounding
startX: (root.width - wrapper.width) / 2 - rounding
startY: 0
wrapper: root.panels.settings
}
}
+124 -112
View File
@@ -15,86 +15,92 @@ import qs.Drawers
Variants {
model: Quickshell.screens
Scope {
id: scope
required property var modelData
PanelWindow {
id: bar
property bool trayMenuVisible: false
screen: scope.modelData
color: "transparent"
property var root: Quickshell.shellDir
WlrLayershell.namespace: "ZShell-Bar"
WlrLayershell.exclusionMode: ExclusionMode.Ignore
PanelWindow {
id: bar
property var root: Quickshell.shellDir
property bool trayMenuVisible: false
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.sidebar || visibilities.dashboard ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
WlrLayershell.namespace: "ZShell-Bar"
color: "transparent"
contentItem.focus: true
screen: scope.modelData
contentItem.Keys.onEscapePressed: {
if ( Config.barConfig.autoHide )
mask: Region {
id: region
property list<Region> nullRegions: []
height: bar.screen.height - backgroundRect.implicitHeight
intersection: Intersection.Xor
regions: popoutRegions.instances
width: bar.width
x: 0
y: Config.barConfig.autoHide && !visibilities.bar ? 4 : 34
}
contentItem.Keys.onEscapePressed: {
if (Config.barConfig.autoHide)
visibilities.bar = false;
visibilities.sidebar = false;
visibilities.dashboard = false;
visibilities.osd = false;
}
PanelWindow {
id: exclusionZone
WlrLayershell.namespace: "ZShell-Bar-Exclusion"
screen: bar.screen
WlrLayershell.layer: WlrLayer.Bottom
PanelWindow {
id: exclusionZone
WlrLayershell.exclusionMode: Config.barConfig.autoHide ? ExclusionMode.Ignore : ExclusionMode.Auto
anchors {
left: true
right: true
top: true
}
color: "transparent"
implicitHeight: 34
}
WlrLayershell.layer: WlrLayer.Bottom
WlrLayershell.namespace: "ZShell-Bar-Exclusion"
color: "transparent"
implicitHeight: 34
screen: bar.screen
anchors {
top: true
left: true
right: true
bottom: true
}
anchors {
left: true
right: true
top: true
}
}
mask: Region {
id: region
x: 0
y: Config.barConfig.autoHide && !visibilities.bar ? 4 : 34
anchors {
bottom: true
left: true
right: true
top: true
}
property list<Region> nullRegions: []
Variants {
id: popoutRegions
width: bar.width
height: bar.screen.height - backgroundRect.implicitHeight
intersection: Intersection.Xor
model: panels.children
regions: popoutRegions.instances
}
Region {
required property Item modelData
Variants {
id: popoutRegions
model: panels.children
Region {
required property Item modelData
x: modelData.x
y: modelData.y + backgroundRect.implicitHeight
width: modelData.width
height: modelData.height
intersection: Intersection.Subtract
}
}
height: modelData.height
intersection: Intersection.Subtract
width: modelData.width
x: modelData.x
y: modelData.y + backgroundRect.implicitHeight
}
}
HyprlandFocusGrab {
id: focusGrab
active: visibilities.launcher || visibilities.sidebar || visibilities.dashboard || ( panels.popouts.hasCurrent && panels.popouts.currentName.startsWith( "traymenu" ))
active: visibilities.launcher || visibilities.sidebar || visibilities.dashboard || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu"))
windows: [bar]
onCleared: {
visibilities.launcher = false;
visibilities.sidebar = false;
@@ -107,92 +113,98 @@ Variants {
PersistentProperties {
id: visibilities
property bool sidebar
property bool dashboard
property bool bar
property bool osd
property bool dashboard
property bool launcher
property bool notif: NotifServer.popups.length > 0
property bool osd
property bool settings
property bool sidebar
Component.onCompleted: Visibilities.load(scope.modelData, this)
}
Binding {
target: visibilities
property: "bar"
target: visibilities
value: visibilities.sidebar || visibilities.dashboard || visibilities.osd || visibilities.notif
when: Config.barConfig.autoHide
}
Item {
anchors.fill: parent
opacity: Appearance.transparency.enabled ? DynamicColors.transparency.base : 1
layer.enabled: true
layer.effect: MultiEffect {
shadowEnabled: true
blurMax: 32
shadowColor: Qt.alpha(DynamicColors.palette.m3shadow, 1)
}
Item {
anchors.fill: parent
layer.enabled: true
opacity: Appearance.transparency.enabled ? DynamicColors.transparency.base : 1
Border {
bar: backgroundRect
layer.effect: MultiEffect {
blurMax: 32
shadowColor: Qt.alpha(DynamicColors.palette.m3shadow, 1)
shadowEnabled: true
}
Border {
bar: backgroundRect
visibilities: visibilities
}
}
Backgrounds {
Backgrounds {
bar: backgroundRect
panels: panels
visibilities: visibilities
panels: panels
bar: backgroundRect
}
}
}
}
Interactions {
Interactions {
id: mouseArea
screen: scope.modelData
popouts: panels.popouts
visibilities: visibilities
panels: panels
anchors.fill: parent
bar: barLoader
anchors.fill: parent
panels: panels
popouts: panels.popouts
screen: scope.modelData
visibilities: visibilities
Panels {
id: panels
screen: scope.modelData
bar: backgroundRect
Panels {
id: panels
bar: backgroundRect
screen: scope.modelData
visibilities: visibilities
}
}
CustomRect {
id: backgroundRect
property Wrapper popouts: panels.popouts
CustomRect {
id: backgroundRect
property Wrapper popouts: panels.popouts
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: 34
anchors.topMargin: Config.barConfig.autoHide && !visibilities.bar ? -30 : 0
color: "transparent"
radius: 0
Behavior on color {
CAnim {}
}
color: "transparent"
implicitHeight: 34
radius: 0
Behavior on anchors.topMargin {
Anim {}
Anim {
}
}
Behavior on color {
CAnim {
}
}
BarLoader {
id: barLoader
anchors.fill: parent
popouts: panels.popouts
bar: bar
visibilities: visibilities
BarLoader {
id: barLoader
anchors.fill: parent
bar: bar
popouts: panels.popouts
screen: scope.modelData
}
}
}
}
}
visibilities: visibilities
}
}
}
}
}
}
+224 -227
View File
@@ -5,53 +5,52 @@ import qs.Config
import qs.Modules as BarPopouts
CustomMouseArea {
id: root
id: root
required property ShellScreen screen
required property BarPopouts.Wrapper popouts
required property PersistentProperties visibilities
required property Panels panels
required property Item bar
required property Item bar
property bool dashboardShortcutActive
property point dragStart
property bool osdShortcutActive
required property Panels panels
required property BarPopouts.Wrapper popouts
required property ShellScreen screen
property bool utilitiesShortcutActive
required property PersistentProperties visibilities
property point dragStart
property bool dashboardShortcutActive
property bool osdShortcutActive
property bool utilitiesShortcutActive
function inBottomPanel(panel: Item, x: real, y: real): bool {
return y > root.height - panel.height && withinPanelWidth(panel, x, y);
}
function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = panel.y + bar.implicitHeight;
return y >= panelY && y <= panelY + panel.height;
}
function inLeftPanel(panel: Item, x: real, y: real): bool {
return x < panel.x + panel.width && withinPanelHeight(panel, x, y);
}
function withinPanelWidth(panel: Item, x: real, y: real): bool {
const panelX = panel.x;
return x >= panelX && x <= panelX + panel.width;
}
function inRightPanel(panel: Item, x: real, y: real): bool {
return x > panel.x && withinPanelHeight(panel, x, y);
}
function inLeftPanel(panel: Item, x: real, y: real): bool {
return x < panel.x + panel.width && withinPanelHeight(panel, x, y);
}
function inTopPanel(panel: Item, x: real, y: real): bool {
return y < bar.implicitHeight + panel.height && withinPanelWidth(panel, x, y);
}
function inRightPanel(panel: Item, x: real, y: real): bool {
return x > panel.x && withinPanelHeight(panel, x, y);
}
function onWheel(event: WheelEvent): void {
if (event.x < bar.implicitWidth) {
bar.handleWheel(event.y, event.angleDelta);
}
}
function inTopPanel(panel: Item, x: real, y: real): bool {
return y < bar.implicitHeight + panel.height && withinPanelWidth(panel, x, y);
}
function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = panel.y + bar.implicitHeight;
return y >= panelY && y <= panelY + panel.height;
}
function inBottomPanel(panel: Item, x: real, y: real): bool {
return y > root.height - panel.height && withinPanelWidth(panel, x, y);
}
function withinPanelWidth(panel: Item, x: real, y: real): bool {
const panelX = panel.x;
return x >= panelX && x <= panelX + panel.width;
}
function onWheel(event: WheelEvent): void {
if (event.x < bar.implicitWidth) {
bar.handleWheel(event.y, event.angleDelta);
}
}
anchors.fill: parent
hoverEnabled: true
anchors.fill: parent
hoverEnabled: true
// onPressed: event => {
// if ( root.popouts.hasCurrent && !inTopPanel( root.popouts, event.x, event.y )) {
@@ -63,216 +62,214 @@ CustomMouseArea {
// }
// }
onContainsMouseChanged: {
if (!containsMouse) {
// Only hide if not activated by shortcut
if (!osdShortcutActive) {
visibilities.osd = false;
root.panels.osd.hovered = false;
}
onContainsMouseChanged: {
if (!containsMouse) {
// Only hide if not activated by shortcut
if (!osdShortcutActive) {
visibilities.osd = false;
root.panels.osd.hovered = false;
}
if (!popouts.currentName.startsWith("traymenu")) {
popouts.hasCurrent = false;
}
if (!popouts.currentName.startsWith("traymenu")) {
popouts.hasCurrent = false;
}
if (Config.barConfig.autoHide && !root.visibilities.sidebar && !root.visibilities.dashboard)
root.visibilities.bar = false;
}
}
if (Config.barConfig.autoHide && !root.visibilities.sidebar && !root.visibilities.dashboard)
root.visibilities.bar = false;
}
}
onPositionChanged: event => {
if (popouts.isDetached)
return;
onPositionChanged: event => {
if (popouts.isDetached)
return;
const x = event.x;
const y = event.y;
const dragX = x - dragStart.x;
const dragY = y - dragStart.y;
const x = event.x;
const y = event.y;
const dragX = x - dragStart.x;
const dragY = y - dragStart.y;
// Show bar in non-exclusive mode on hover
if (!visibilities.bar && Config.barConfig.autoHide && y < bar.implicitHeight + bar.anchors.topMargin)
visibilities.bar = true;
// Show bar in non-exclusive mode on hover
if (!visibilities.bar && Config.barConfig.autoHide && y < bar.implicitHeight + bar.anchors.topMargin)
visibilities.bar = true;
if (panels.sidebar.width === 0) {
// Show osd on hover
const showOsd = inRightPanel(panels.osd, x, y);
if (panels.sidebar.width === 0) {
// Show osd on hover
const showOsd = inRightPanel(panels.osd, x, y);
// // Always update visibility based on hover if not in shortcut mode
if (!osdShortcutActive) {
visibilities.osd = showOsd;
root.panels.osd.hovered = showOsd;
} else if (showOsd) {
// If hovering over OSD area while in shortcut mode, transition to hover control
osdShortcutActive = false;
root.panels.osd.hovered = true;
}
// // Always update visibility based on hover if not in shortcut mode
if (!osdShortcutActive) {
visibilities.osd = showOsd;
root.panels.osd.hovered = showOsd;
} else if (showOsd) {
// If hovering over OSD area while in shortcut mode, transition to hover control
osdShortcutActive = false;
root.panels.osd.hovered = true;
}
// const showSidebar = pressed && dragStart.x > bar.implicitWidth + panels.sidebar.x;
//
// // Show/hide session on drag
// if (pressed && inRightPanel(panels.session, dragStart.x, dragStart.y) && withinPanelHeight(panels.session, x, y)) {
// if (dragX < -Config.session.dragThreshold)
// visibilities.session = true;
// else if (dragX > Config.session.dragThreshold)
// visibilities.session = false;
//
// // Show sidebar on drag if in session area and session is nearly fully visible
// if (showSidebar && panels.session.width >= panels.session.nonAnimWidth && dragX < -Config.sidebar.dragThreshold)
// visibilities.sidebar = true;
// } else if (showSidebar && dragX < -Config.sidebar.dragThreshold) {
// // Show sidebar on drag if not in session area
// visibilities.sidebar = true;
// }
} else {
const outOfSidebar = x < width - panels.sidebar.width;
// Show osd on hover
const showOsd = outOfSidebar && inRightPanel(panels.osd, x, y);
// const showSidebar = pressed && dragStart.x > bar.implicitWidth + panels.sidebar.x;
//
// // Show/hide session on drag
// if (pressed && inRightPanel(panels.session, dragStart.x, dragStart.y) && withinPanelHeight(panels.session, x, y)) {
// if (dragX < -Config.session.dragThreshold)
// visibilities.session = true;
// else if (dragX > Config.session.dragThreshold)
// visibilities.session = false;
//
// // Show sidebar on drag if in session area and session is nearly fully visible
// if (showSidebar && panels.session.width >= panels.session.nonAnimWidth && dragX < -Config.sidebar.dragThreshold)
// visibilities.sidebar = true;
// } else if (showSidebar && dragX < -Config.sidebar.dragThreshold) {
// // Show sidebar on drag if not in session area
// visibilities.sidebar = true;
// }
} else {
const outOfSidebar = x < width - panels.sidebar.width;
// Show osd on hover
const showOsd = outOfSidebar && inRightPanel(panels.osd, x, y);
// Always update visibility based on hover if not in shortcut mode
if (!osdShortcutActive) {
visibilities.osd = showOsd;
root.panels.osd.hovered = showOsd;
} else if (showOsd) {
// If hovering over OSD area while in shortcut mode, transition to hover control
osdShortcutActive = false;
root.panels.osd.hovered = true;
}
//
// // Show/hide session on drag
// if (pressed && outOfSidebar && inRightPanel(panels.session, dragStart.x, dragStart.y) && withinPanelHeight(panels.session, x, y)) {
// if (dragX < -Config.session.dragThreshold)
// visibilities.session = true;
// else if (dragX > Config.session.dragThreshold)
// visibilities.session = false;
// }
//
// // Hide sidebar on drag
// if (pressed && inRightPanel(panels.sidebar, dragStart.x, 0) && dragX > Config.sidebar.dragThreshold)
// visibilities.sidebar = false;
}
// Always update visibility based on hover if not in shortcut mode
if (!osdShortcutActive) {
visibilities.osd = showOsd;
root.panels.osd.hovered = showOsd;
} else if (showOsd) {
// If hovering over OSD area while in shortcut mode, transition to hover control
osdShortcutActive = false;
root.panels.osd.hovered = true;
}
//
// // Show/hide session on drag
// if (pressed && outOfSidebar && inRightPanel(panels.session, dragStart.x, dragStart.y) && withinPanelHeight(panels.session, x, y)) {
// if (dragX < -Config.session.dragThreshold)
// visibilities.session = true;
// else if (dragX > Config.session.dragThreshold)
// visibilities.session = false;
// }
//
// // Hide sidebar on drag
// if (pressed && inRightPanel(panels.sidebar, dragStart.x, 0) && dragX > Config.sidebar.dragThreshold)
// visibilities.sidebar = false;
}
// Show launcher on hover, or show/hide on drag if hover is disabled
// if (Config.launcher.showOnHover) {
// if (!visibilities.launcher && inBottomPanel(panels.launcher, x, y))
// visibilities.launcher = true;
// } else if (pressed && inBottomPanel(panels.launcher, dragStart.x, dragStart.y) && withinPanelWidth(panels.launcher, x, y)) {
// if (dragY < -Config.launcher.dragThreshold)
// visibilities.launcher = true;
// else if (dragY > Config.launcher.dragThreshold)
// visibilities.launcher = false;
// }
//
// // Show dashboard on hover
// const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboard, x, y);
//
// // Always update visibility based on hover if not in shortcut mode
// if (!dashboardShortcutActive) {
// visibilities.dashboard = showDashboard;
// } else if (showDashboard) {
// // If hovering over dashboard area while in shortcut mode, transition to hover control
// dashboardShortcutActive = false;
// }
//
// // Show/hide dashboard on drag (for touchscreen devices)
// if (pressed && inTopPanel(panels.dashboard, dragStart.x, dragStart.y) && withinPanelWidth(panels.dashboard, x, y)) {
// if (dragY > Config.dashboard.dragThreshold)
// visibilities.dashboard = true;
// else if (dragY < -Config.dashboard.dragThreshold)
// visibilities.dashboard = false;
// }
//
// // Show utilities on hover
// const showUtilities = inBottomPanel(panels.utilities, x, y);
//
// // Always update visibility based on hover if not in shortcut mode
// if (!utilitiesShortcutActive) {
// visibilities.utilities = showUtilities;
// } else if (showUtilities) {
// // If hovering over utilities area while in shortcut mode, transition to hover control
// utilitiesShortcutActive = false;
// }
// Show launcher on hover, or show/hide on drag if hover is disabled
// if (Config.launcher.showOnHover) {
// if (!visibilities.launcher && inBottomPanel(panels.launcher, x, y))
// visibilities.launcher = true;
// } else if (pressed && inBottomPanel(panels.launcher, dragStart.x, dragStart.y) && withinPanelWidth(panels.launcher, x, y)) {
// if (dragY < -Config.launcher.dragThreshold)
// visibilities.launcher = true;
// else if (dragY > Config.launcher.dragThreshold)
// visibilities.launcher = false;
// }
//
// // Show dashboard on hover
// const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboard, x, y);
//
// // Always update visibility based on hover if not in shortcut mode
// if (!dashboardShortcutActive) {
// visibilities.dashboard = showDashboard;
// } else if (showDashboard) {
// // If hovering over dashboard area while in shortcut mode, transition to hover control
// dashboardShortcutActive = false;
// }
//
// // Show/hide dashboard on drag (for touchscreen devices)
// if (pressed && inTopPanel(panels.dashboard, dragStart.x, dragStart.y) && withinPanelWidth(panels.dashboard, x, y)) {
// if (dragY > Config.dashboard.dragThreshold)
// visibilities.dashboard = true;
// else if (dragY < -Config.dashboard.dragThreshold)
// visibilities.dashboard = false;
// }
//
// // Show utilities on hover
// const showUtilities = inBottomPanel(panels.utilities, x, y);
//
// // Always update visibility based on hover if not in shortcut mode
// if (!utilitiesShortcutActive) {
// visibilities.utilities = showUtilities;
// } else if (showUtilities) {
// // If hovering over utilities area while in shortcut mode, transition to hover control
// utilitiesShortcutActive = false;
// }
// Show popouts on hover
if (y < bar.implicitHeight) {
bar.checkPopout(x);
}
}
// Show popouts on hover
if (y < bar.implicitHeight) {
bar.checkPopout(x);
}
}
// Monitor individual visibility changes
Connections {
function onDashboardChanged() {
if (root.visibilities.dashboard) {
// Dashboard became visible, immediately check if this should be shortcut mode
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
if (!inDashboardArea) {
root.dashboardShortcutActive = true;
}
// Monitor individual visibility changes
Connections {
target: root.visibilities
root.visibilities.sidebar = false;
root.popouts.hasCurrent = false;
} else {
// Dashboard hidden, clear shortcut flag
root.dashboardShortcutActive = false;
// root.visibilities.bar = false;
}
}
function onLauncherChanged() {
// If launcher is hidden, clear shortcut flags for dashboard and OSD
if (!root.visibilities.launcher) {
root.dashboardShortcutActive = false;
root.osdShortcutActive = false;
root.utilitiesShortcutActive = false;
function onLauncherChanged() {
// If launcher is hidden, clear shortcut flags for dashboard and OSD
if (!root.visibilities.launcher) {
root.dashboardShortcutActive = false;
root.osdShortcutActive = false;
root.utilitiesShortcutActive = false;
// Also hide dashboard and OSD if they're not being hovered
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
// Also hide dashboard and OSD if they're not being hovered
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
if (!inDashboardArea) {
root.visibilities.dashboard = false;
}
if (!inOsdArea) {
root.visibilities.osd = false;
root.panels.osd.hovered = false;
}
}
}
if (!inDashboardArea) {
root.visibilities.dashboard = false;
}
if (!inOsdArea) {
root.visibilities.osd = false;
root.panels.osd.hovered = false;
}
}
}
function onOsdChanged() {
if (root.visibilities.osd) {
// OSD became visible, immediately check if this should be shortcut mode
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
console.log(inOsdArea);
if (!inOsdArea) {
root.osdShortcutActive = true;
}
} else {
// OSD hidden, clear shortcut flag
root.osdShortcutActive = false;
}
}
function onSidebarChanged() {
if ( root.visibilities.sidebar ) {
if (root.visibilities.sidebar) {
root.visibilities.dashboard = false;
root.popouts.hasCurrent = false;
}
}
function onDashboardChanged() {
if (root.visibilities.dashboard) {
// Dashboard became visible, immediately check if this should be shortcut mode
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
if (!inDashboardArea) {
root.dashboardShortcutActive = true;
}
function onUtilitiesChanged() {
if (root.visibilities.utilities) {
// Utilities became visible, immediately check if this should be shortcut mode
const inUtilitiesArea = root.inBottomPanel(root.panels.utilities, root.mouseX, root.mouseY);
if (!inUtilitiesArea) {
root.utilitiesShortcutActive = true;
}
} else {
// Utilities hidden, clear shortcut flag
root.utilitiesShortcutActive = false;
}
}
root.visibilities.sidebar = false;
root.popouts.hasCurrent = false;
} else {
// Dashboard hidden, clear shortcut flag
root.dashboardShortcutActive = false;
// root.visibilities.bar = false;
}
}
function onOsdChanged() {
if (root.visibilities.osd) {
// OSD became visible, immediately check if this should be shortcut mode
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
console.log(inOsdArea);
if (!inOsdArea) {
root.osdShortcutActive = true;
}
} else {
// OSD hidden, clear shortcut flag
root.osdShortcutActive = false;
}
}
function onUtilitiesChanged() {
if (root.visibilities.utilities) {
// Utilities became visible, immediately check if this should be shortcut mode
const inUtilitiesArea = root.inBottomPanel(root.panels.utilities, root.mouseX, root.mouseY);
if (!inUtilitiesArea) {
root.utilitiesShortcutActive = true;
}
} else {
// Utilities hidden, clear shortcut flag
root.utilitiesShortcutActive = false;
}
}
}
target: root.visibilities
}
}
+49 -57
View File
@@ -13,124 +13,116 @@ import qs.Modules.Settings as Settings
import qs.Config
Item {
id: root
id: root
required property ShellScreen screen
required property Item bar
required property Item bar
readonly property alias dashboard: dashboard
readonly property alias launcher: launcher
readonly property alias notifications: notifications
readonly property alias osd: osd
readonly property alias popouts: popouts
required property ShellScreen screen
readonly property alias settings: settings
readonly property alias sidebar: sidebar
readonly property alias toasts: toasts
readonly property alias utilities: utilities
required property PersistentProperties visibilities
readonly property alias popouts: popouts
readonly property alias sidebar: sidebar
readonly property alias notifications: notifications
readonly property alias utilities: utilities
readonly property alias dashboard: dashboard
readonly property alias osd: osd
readonly property alias toasts: toasts
readonly property alias launcher: launcher
readonly property alias settings: settings
anchors.fill: parent
// anchors.margins: 8
anchors.topMargin: Config.barConfig.autoHide && !visibilities.bar ? 0 : bar.implicitHeight
anchors.fill: parent
// anchors.margins: 8
anchors.topMargin: Config.barConfig.autoHide && !visibilities.bar ? 0 : bar.implicitHeight
Behavior on anchors.topMargin {
Anim {}
Anim {
}
}
Osd.Wrapper {
id: osd
anchors.right: parent.right
anchors.rightMargin: sidebar.width
anchors.verticalCenter: parent.verticalCenter
clip: sidebar.width > 0
screen: root.screen
visibilities: root.visibilities
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: sidebar.width
}
Modules.Wrapper {
id: popouts
Modules.Wrapper {
id: popouts
screen: root.screen
anchors.top: parent.top
x: {
const off = currentCenter - nonAnimWidth / 2;
const diff = root.width - Math.floor(off + nonAnimWidth);
if ( diff < 0 )
return off + diff;
return Math.floor( Math.max( off, 0 ));
}
}
anchors.top: parent.top
screen: root.screen
x: {
const off = currentCenter - nonAnimWidth / 2;
const diff = root.width - Math.floor(off + nonAnimWidth);
if (diff < 0)
return off + diff;
return Math.floor(Math.max(off, 0));
}
}
Toasts.Toasts {
id: toasts
anchors.bottom: sidebar.visible ? parent.bottom : utilities.top
anchors.right: sidebar.left
anchors.margins: Appearance.padding.normal
anchors.right: sidebar.left
}
Notifications.Wrapper {
id: notifications
visibilities: root.visibilities
panels: root
anchors.top: parent.top
anchors.right: parent.right
anchors.top: parent.top
panels: root
visibilities: root.visibilities
}
Launcher.Wrapper {
id: launcher
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
panels: root
screen: root.screen
visibilities: root.visibilities
panels: root
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
}
Utils.Wrapper {
id: utilities
visibilities: root.visibilities
sidebar: sidebar
popouts: popouts
anchors.bottom: parent.bottom
anchors.right: parent.right
popouts: popouts
sidebar: sidebar
visibilities: root.visibilities
}
Dashboard.Wrapper {
id: dashboard
visibilities: root.visibilities
anchors.right: parent.right
anchors.top: parent.top
visibilities: root.visibilities
}
Sidebar.Wrapper {
id: sidebar
visibilities: root.visibilities
panels: root
anchors.top: notifications.bottom
anchors.bottom: utilities.top
anchors.right: parent.right
anchors.top: notifications.bottom
panels: root
visibilities: root.visibilities
}
Settings.Wrapper {
id: settings
visibilities: root.visibilities
panels: root
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
panels: root
visibilities: root.visibilities
}
}
+26 -23
View File
@@ -2,29 +2,32 @@ import QtQuick
import QtQuick.Effects
Item {
id: root
property real radius
id: root
Rectangle {
id: shadowRect
anchors.fill: root
radius: root.radius
layer.enabled: true
color: "black"
visible: false
}
property real radius
MultiEffect {
id: effects
source: shadowRect
anchors.fill: shadowRect
shadowBlur: 2.0
shadowEnabled: true
shadowOpacity: 1
shadowColor: "black"
maskSource: shadowRect
maskEnabled: true
maskInverted: true
autoPaddingEnabled: true
}
Rectangle {
id: shadowRect
anchors.fill: root
color: "black"
layer.enabled: true
radius: root.radius
visible: false
}
MultiEffect {
id: effects
anchors.fill: shadowRect
autoPaddingEnabled: true
maskEnabled: true
maskInverted: true
maskSource: shadowRect
shadowBlur: 2.0
shadowColor: "black"
shadowEnabled: true
shadowOpacity: 1
source: shadowRect
}
}
+54 -50
View File
@@ -8,49 +8,49 @@ import ZShell
import qs.Components
Scope {
LazyLoader {
id: root
LazyLoader {
id: root
property bool freeze
property bool closing
property bool closing
property bool freeze
Variants {
model: Quickshell.screens
PanelWindow {
id: win
color: "transparent"
Variants {
model: Quickshell.screens
required property ShellScreen modelData
PanelWindow {
id: win
screen: modelData
WlrLayershell.namespace: "areapicker"
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: root.closing ? WlrKeyboardFocus.None : WlrKeyboardFocus.Exclusive
mask: root.closing ? empty : null
required property ShellScreen modelData
anchors {
top: true
bottom: true
left: true
right: true
}
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: root.closing ? WlrKeyboardFocus.None : WlrKeyboardFocus.Exclusive
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.namespace: "areapicker"
color: "transparent"
mask: root.closing ? empty : null
screen: modelData
Region {
id: empty
}
anchors {
bottom: true
left: true
right: true
top: true
}
Picker {
loader: root
screen: win.modelData
}
}
}
}
Region {
id: empty
}
Picker {
loader: root
screen: win.modelData
}
}
}
}
IpcHandler {
target: "picker"
function open(): void {
root.freeze = false;
root.closing = false;
@@ -62,23 +62,27 @@ Scope {
root.closing = false;
root.activeAsync = true;
}
target: "picker"
}
CustomShortcut {
name: "screenshot"
onPressed: {
root.freeze = false;
root.closing = false;
root.activeAsync = true;
}
}
CustomShortcut {
name: "screenshot"
CustomShortcut {
name: "screenshotFreeze"
onPressed: {
root.freeze = true;
root.closing = false;
root.activeAsync = true;
}
}
onPressed: {
root.freeze = false;
root.closing = false;
root.activeAsync = true;
}
}
CustomShortcut {
name: "screenshotFreeze"
onPressed: {
root.freeze = true;
root.closing = false;
root.activeAsync = true;
}
}
}
+180 -176
View File
@@ -8,219 +8,223 @@ import qs.Config
import qs.Components
Singleton {
id: root
id: root
property list<var> ddcMonitors: []
readonly property list<Monitor> monitors: variants.instances
property bool appleDisplayPresent: false
property bool appleDisplayPresent: false
property list<var> ddcMonitors: []
readonly property list<Monitor> monitors: variants.instances
function getMonitorForScreen(screen: ShellScreen): var {
return monitors.find(m => m.modelData === screen);
}
function decreaseBrightness(): void {
const monitor = getMonitor("active");
if (monitor)
monitor.setBrightness(monitor.brightness - Config.services.brightnessIncrement);
}
function getMonitor(query: string): var {
if (query === "active") {
return monitors.find(m => Hypr.monitorFor(m.modelData)?.focused);
}
function getMonitor(query: string): var {
if (query === "active") {
return monitors.find(m => Hypr.monitorFor(m.modelData)?.focused);
}
if (query.startsWith("model:")) {
const model = query.slice(6);
return monitors.find(m => m.modelData.model === model);
}
if (query.startsWith("model:")) {
const model = query.slice(6);
return monitors.find(m => m.modelData.model === model);
}
if (query.startsWith("serial:")) {
const serial = query.slice(7);
return monitors.find(m => m.modelData.serialNumber === serial);
}
if (query.startsWith("serial:")) {
const serial = query.slice(7);
return monitors.find(m => m.modelData.serialNumber === serial);
}
if (query.startsWith("id:")) {
const id = parseInt(query.slice(3), 10);
return monitors.find(m => Hypr.monitorFor(m.modelData)?.id === id);
}
if (query.startsWith("id:")) {
const id = parseInt(query.slice(3), 10);
return monitors.find(m => Hypr.monitorFor(m.modelData)?.id === id);
}
return monitors.find(m => m.modelData.name === query);
}
return monitors.find(m => m.modelData.name === query);
}
function increaseBrightness(): void {
const monitor = getMonitor("active");
if (monitor)
monitor.setBrightness(monitor.brightness + Config.services.brightnessIncrement);
}
function getMonitorForScreen(screen: ShellScreen): var {
return monitors.find(m => m.modelData === screen);
}
function decreaseBrightness(): void {
const monitor = getMonitor("active");
if (monitor)
monitor.setBrightness(monitor.brightness - Config.services.brightnessIncrement);
}
function increaseBrightness(): void {
const monitor = getMonitor("active");
if (monitor)
monitor.setBrightness(monitor.brightness + Config.services.brightnessIncrement);
}
onMonitorsChanged: {
ddcMonitors = [];
ddcProc.running = true;
}
onMonitorsChanged: {
ddcMonitors = [];
ddcProc.running = true;
}
Variants {
id: variants
Variants {
id: variants
model: Quickshell.screens
model: Quickshell.screens
Monitor {}
}
Monitor {
}
}
Process {
running: true
command: ["sh", "-c", "asdbctl get"] // To avoid warnings if asdbctl is not installed
stdout: StdioCollector {
onStreamFinished: root.appleDisplayPresent = text.trim().length > 0
}
}
Process {
command: ["sh", "-c", "asdbctl get"] // To avoid warnings if asdbctl is not installed
running: true
Process {
id: ddcProc
stdout: StdioCollector {
onStreamFinished: root.appleDisplayPresent = text.trim().length > 0
}
}
command: ["ddcutil", "detect", "--brief"]
stdout: StdioCollector {
onStreamFinished: root.ddcMonitors = text.trim().split("\n\n").filter(d => d.startsWith("Display ")).map(d => ({
busNum: d.match(/I2C bus:[ ]*\/dev\/i2c-([0-9]+)/)[1],
connector: d.match(/DRM connector:\s+(.*)/)[1].replace(/^card\d+-/, "") // strip "card1-"
}))
}
}
Process {
id: ddcProc
CustomShortcut {
name: "brightnessUp"
description: "Increase brightness"
onPressed: root.increaseBrightness()
}
command: ["ddcutil", "detect", "--brief"]
CustomShortcut {
name: "brightnessDown"
description: "Decrease brightness"
onPressed: root.decreaseBrightness()
}
stdout: StdioCollector {
onStreamFinished: root.ddcMonitors = text.trim().split("\n\n").filter(d => d.startsWith("Display ")).map(d => ({
busNum: d.match(/I2C bus:[ ]*\/dev\/i2c-([0-9]+)/)[1],
connector: d.match(/DRM connector:\s+(.*)/)[1].replace(/^card\d+-/, "") // strip "card1-"
}))
}
}
IpcHandler {
target: "brightness"
CustomShortcut {
description: "Increase brightness"
name: "brightnessUp"
function get(): real {
return getFor("active");
}
onPressed: root.increaseBrightness()
}
// Allows searching by active/model/serial/id/name
function getFor(query: string): real {
return root.getMonitor(query)?.brightness ?? -1;
}
CustomShortcut {
description: "Decrease brightness"
name: "brightnessDown"
function set(value: string): string {
return setFor("active", value);
}
onPressed: root.decreaseBrightness()
}
// Handles brightness value like brightnessctl: 0.1, +0.1, 0.1-, 10%, +10%, 10%-
function setFor(query: string, value: string): string {
const monitor = root.getMonitor(query);
if (!monitor)
return "Invalid monitor: " + query;
IpcHandler {
function get(): real {
return getFor("active");
}
let targetBrightness;
if (value.endsWith("%-")) {
const percent = parseFloat(value.slice(0, -2));
targetBrightness = monitor.brightness - (percent / 100);
} else if (value.startsWith("+") && value.endsWith("%")) {
const percent = parseFloat(value.slice(1, -1));
targetBrightness = monitor.brightness + (percent / 100);
} else if (value.endsWith("%")) {
const percent = parseFloat(value.slice(0, -1));
targetBrightness = percent / 100;
} else if (value.startsWith("+")) {
const increment = parseFloat(value.slice(1));
targetBrightness = monitor.brightness + increment;
} else if (value.endsWith("-")) {
const decrement = parseFloat(value.slice(0, -1));
targetBrightness = monitor.brightness - decrement;
} else if (value.includes("%") || value.includes("-") || value.includes("+")) {
return `Invalid brightness format: ${value}\nExpected: 0.1, +0.1, 0.1-, 10%, +10%, 10%-`;
} else {
targetBrightness = parseFloat(value);
}
// Allows searching by active/model/serial/id/name
function getFor(query: string): real {
return root.getMonitor(query)?.brightness ?? -1;
}
if (isNaN(targetBrightness))
return `Failed to parse value: ${value}\nExpected: 0.1, +0.1, 0.1-, 10%, +10%, 10%-`;
function set(value: string): string {
return setFor("active", value);
}
monitor.setBrightness(targetBrightness);
// Handles brightness value like brightnessctl: 0.1, +0.1, 0.1-, 10%, +10%, 10%-
function setFor(query: string, value: string): string {
const monitor = root.getMonitor(query);
if (!monitor)
return "Invalid monitor: " + query;
return `Set monitor ${monitor.modelData.name} brightness to ${+monitor.brightness.toFixed(2)}`;
}
}
let targetBrightness;
if (value.endsWith("%-")) {
const percent = parseFloat(value.slice(0, -2));
targetBrightness = monitor.brightness - (percent / 100);
} else if (value.startsWith("+") && value.endsWith("%")) {
const percent = parseFloat(value.slice(1, -1));
targetBrightness = monitor.brightness + (percent / 100);
} else if (value.endsWith("%")) {
const percent = parseFloat(value.slice(0, -1));
targetBrightness = percent / 100;
} else if (value.startsWith("+")) {
const increment = parseFloat(value.slice(1));
targetBrightness = monitor.brightness + increment;
} else if (value.endsWith("-")) {
const decrement = parseFloat(value.slice(0, -1));
targetBrightness = monitor.brightness - decrement;
} else if (value.includes("%") || value.includes("-") || value.includes("+")) {
return `Invalid brightness format: ${value}\nExpected: 0.1, +0.1, 0.1-, 10%, +10%, 10%-`;
} else {
targetBrightness = parseFloat(value);
}
component Monitor: QtObject {
id: monitor
if (isNaN(targetBrightness))
return `Failed to parse value: ${value}\nExpected: 0.1, +0.1, 0.1-, 10%, +10%, 10%-`;
required property ShellScreen modelData
readonly property bool isDdc: root.ddcMonitors.some(m => m.connector === modelData.name)
readonly property string busNum: root.ddcMonitors.find(m => m.connector === modelData.name)?.busNum ?? ""
readonly property bool isAppleDisplay: root.appleDisplayPresent && modelData.model.startsWith("StudioDisplay")
property real brightness
property real queuedBrightness: NaN
monitor.setBrightness(targetBrightness);
readonly property Process initProc: Process {
stdout: StdioCollector {
onStreamFinished: {
if (monitor.isAppleDisplay) {
const val = parseInt(text.trim());
monitor.brightness = val / 101;
} else {
const [, , , cur, max] = text.split(" ");
monitor.brightness = parseInt(cur) / parseInt(max);
}
}
}
}
return `Set monitor ${monitor.modelData.name} brightness to ${+monitor.brightness.toFixed(2)}`;
}
readonly property Timer timer: Timer {
interval: 500
onTriggered: {
if (!isNaN(monitor.queuedBrightness)) {
monitor.setBrightness(monitor.queuedBrightness);
monitor.queuedBrightness = NaN;
}
}
}
target: "brightness"
}
function setBrightness(value: real): void {
value = Math.max(0, Math.min(1, value));
const rounded = Math.round(value * 100);
if (Math.round(brightness * 100) === rounded)
return;
component Monitor: QtObject {
id: monitor
if (isDdc && timer.running) {
queuedBrightness = value;
return;
}
property real brightness
readonly property string busNum: root.ddcMonitors.find(m => m.connector === modelData.name)?.busNum ?? ""
readonly property Process initProc: Process {
stdout: StdioCollector {
onStreamFinished: {
if (monitor.isAppleDisplay) {
const val = parseInt(text.trim());
monitor.brightness = val / 101;
} else {
const [, , , cur, max] = text.split(" ");
monitor.brightness = parseInt(cur) / parseInt(max);
}
}
}
}
readonly property bool isAppleDisplay: root.appleDisplayPresent && modelData.model.startsWith("StudioDisplay")
readonly property bool isDdc: root.ddcMonitors.some(m => m.connector === modelData.name)
required property ShellScreen modelData
property real queuedBrightness: NaN
readonly property Timer timer: Timer {
interval: 500
brightness = value;
onTriggered: {
if (!isNaN(monitor.queuedBrightness)) {
monitor.setBrightness(monitor.queuedBrightness);
monitor.queuedBrightness = NaN;
}
}
}
if (isAppleDisplay)
Quickshell.execDetached(["asdbctl", "set", rounded]);
else if (isDdc)
Quickshell.execDetached(["ddcutil", "--disable-dynamic-sleep", "--sleep-multiplier", ".1", "--skip-ddc-checks", "-b", busNum, "setvcp", "10", rounded]);
else
Quickshell.execDetached(["brightnessctl", "s", `${rounded}%`]);
function initBrightness(): void {
if (isAppleDisplay)
initProc.command = ["asdbctl", "get"];
else if (isDdc)
initProc.command = ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"];
else
initProc.command = ["sh", "-c", "echo a b c $(brightnessctl g) $(brightnessctl m)"];
if (isDdc)
timer.restart();
}
initProc.running = true;
}
function initBrightness(): void {
if (isAppleDisplay)
initProc.command = ["asdbctl", "get"];
else if (isDdc)
initProc.command = ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"];
else
initProc.command = ["sh", "-c", "echo a b c $(brightnessctl g) $(brightnessctl m)"];
function setBrightness(value: real): void {
value = Math.max(0, Math.min(1, value));
const rounded = Math.round(value * 100);
if (Math.round(brightness * 100) === rounded)
return;
initProc.running = true;
}
if (isDdc && timer.running) {
queuedBrightness = value;
return;
}
onBusNumChanged: initBrightness()
Component.onCompleted: initBrightness()
}
brightness = value;
if (isAppleDisplay)
Quickshell.execDetached(["asdbctl", "set", rounded]);
else if (isDdc)
Quickshell.execDetached(["ddcutil", "--disable-dynamic-sleep", "--sleep-multiplier", ".1", "--skip-ddc-checks", "-b", busNum, "setvcp", "10", rounded]);
else
Quickshell.execDetached(["brightnessctl", "s", `${rounded}%`]);
if (isDdc)
timer.restart();
}
Component.onCompleted: initBrightness()
onBusNumChanged: initBrightness()
}
}
+15 -15
View File
@@ -4,25 +4,25 @@ import QtQuick
import qs.Paths
Image {
id: root
id: root
property alias path: manager.path
property alias path: manager.path
asynchronous: true
fillMode: Image.PreserveAspectCrop
asynchronous: true
fillMode: Image.PreserveAspectCrop
Connections {
target: QsWindow.window
Connections {
function onDevicePixelRatioChanged(): void {
manager.updateSource();
}
function onDevicePixelRatioChanged(): void {
manager.updateSource();
}
}
target: QsWindow.window
}
CachingImageManager {
id: manager
CachingImageManager {
id: manager
item: root
cacheDir: Qt.resolvedUrl(Paths.imagecache)
}
cacheDir: Qt.resolvedUrl(Paths.imagecache)
item: root
}
}
+48 -50
View File
@@ -10,6 +10,53 @@ Singleton {
property int displayYear: new Date().getFullYear()
readonly property int weekStartDay: 1 // 0 = Sunday, 1 = Monday
function getISOWeekNumber(date: var): int {
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
}
function getWeekNumbers(month: int, year: int): var {
const days = getWeeksForMonth(month, year);
const weekNumbers = [];
let lastWeekNumber = -1;
for (let i = 0; i < days.length; i++) {
// Only add week numbers for days that belong to the current month
if (days[i].isCurrentMonth) {
const dayDate = new Date(days[i].year, days[i].month, days[i].day);
const weekNumber = getISOWeekNumber(dayDate);
// Only push if this is a new week
if (weekNumber !== lastWeekNumber) {
weekNumbers.push(weekNumber);
lastWeekNumber = weekNumber;
}
}
}
return weekNumbers;
}
function getWeekStartIndex(month: int, year: int): int {
const today = new Date();
if (today.getMonth() !== month || today.getFullYear() !== year) {
return 0;
}
const days = getWeeksForMonth(month, year);
for (let i = 0; i < days.length; i++) {
if (days[i].isToday) {
// Return the start index of the week containing today
return Math.floor(i / 7) * 7;
}
}
return 0;
}
function getWeeksForMonth(month: int, year: int): var {
const firstDayOfMonth = new Date(year, month, 1);
const lastDayOfMonth = new Date(year, month + 1, 0);
@@ -43,57 +90,8 @@ Singleton {
return days;
}
function getWeekNumbers(month: int, year: int): var {
const days = getWeeksForMonth(month, year);
const weekNumbers = [];
let lastWeekNumber = -1;
for (let i = 0; i < days.length; i++) {
// Only add week numbers for days that belong to the current month
if (days[i].isCurrentMonth) {
const dayDate = new Date(days[i].year, days[i].month, days[i].day);
const weekNumber = getISOWeekNumber(dayDate);
// Only push if this is a new week
if (weekNumber !== lastWeekNumber) {
weekNumbers.push(weekNumber);
lastWeekNumber = weekNumber;
}
}
}
return weekNumbers;
}
function getISOWeekNumber(date: var): int {
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
}
function isDateToday(date: var): bool {
const today = new Date();
return date.getDate() === today.getDate() &&
date.getMonth() === today.getMonth() &&
date.getFullYear() === today.getFullYear();
}
function getWeekStartIndex(month: int, year: int): int {
const today = new Date();
if (today.getMonth() !== month || today.getFullYear() !== year) {
return 0;
}
const days = getWeeksForMonth(month, year);
for (let i = 0; i < days.length; i++) {
if (days[i].isToday) {
// Return the start index of the week containing today
return Math.floor(i / 7) * 7;
}
}
return 0;
return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear();
}
}
+10 -10
View File
@@ -3,15 +3,15 @@ pragma Singleton
import Quickshell
Singleton {
id: root
id: root
function getTrayIcon(id: string, icon: string): string {
if (icon.includes("?path=")) {
const [name, path] = icon.split("?path=");
icon = Qt.resolvedUrl(`${path}/${name.slice(name.lastIndexOf("/") + 1)}`);
} else if (icon.includes("qspixmap") && id === "chrome_status_icon_1") {
icon = icon.replace("qspixmap", "icon/discord-tray");
}
return icon;
}
function getTrayIcon(id: string, icon: string): string {
if (icon.includes("?path=")) {
const [name, path] = icon.split("?path=");
icon = Qt.resolvedUrl(`${path}/${name.slice(name.lastIndexOf("/") + 1)}`);
} else if (icon.includes("qspixmap") && id === "chrome_status_icon_1") {
icon = icon.replace("qspixmap", "icon/discord-tray");
}
return icon;
}
}
+7 -6
View File
@@ -4,12 +4,13 @@ import Quickshell
import Quickshell.Io
Singleton {
id: root
id: root
property alias hasNotifications: adapter.hasNotifications
property alias hasNotifications: adapter.hasNotifications
JsonObject {
id: adapter
property bool hasNotifications: false
}
JsonObject {
id: adapter
property bool hasNotifications: false
}
}
+127 -129
View File
@@ -9,157 +9,155 @@ import QtQuick
import qs.Components
Singleton {
id: root
id: root
readonly property var toplevels: Hyprland.toplevels
readonly property var workspaces: Hyprland.workspaces
readonly property var monitors: Hyprland.monitors
property string activeName
readonly property HyprlandToplevel activeToplevel: Hyprland.activeToplevel
readonly property int activeWsId: focusedWorkspace?.id ?? 1
property string applicationDir: "/usr/share/applications/"
readonly property bool capsLock: keyboard?.capsLock ?? false
readonly property string defaultKbLayout: keyboard?.layout.split(",")[0] ?? "??"
property string desktopName: ""
readonly property alias devices: extras.devices
readonly property alias extras: extras
readonly property HyprlandMonitor focusedMonitor: Hyprland.focusedMonitor
readonly property HyprlandWorkspace focusedWorkspace: Hyprland.focusedWorkspace
property bool hadKeyboard
readonly property string kbLayout: kbMap.get(kbLayoutFull) ?? "??"
readonly property string kbLayoutFull: keyboard?.activeKeymap ?? "Unknown"
readonly property var kbMap: new Map()
readonly property HyprKeyboard keyboard: extras.devices.keyboards.find(kb => kb.main) ?? null
readonly property var monitors: Hyprland.monitors
readonly property bool numLock: keyboard?.numLock ?? false
readonly property alias options: extras.options
readonly property var toplevels: Hyprland.toplevels
readonly property var workspaces: Hyprland.workspaces
readonly property HyprlandToplevel activeToplevel: Hyprland.activeToplevel
readonly property HyprlandWorkspace focusedWorkspace: Hyprland.focusedWorkspace
readonly property HyprlandMonitor focusedMonitor: Hyprland.focusedMonitor
readonly property int activeWsId: focusedWorkspace?.id ?? 1
signal configReloaded
property string activeName
property string applicationDir: "/usr/share/applications/"
property string desktopName: ""
readonly property HyprKeyboard keyboard: extras.devices.keyboards.find(kb => kb.main) ?? null
readonly property bool capsLock: keyboard?.capsLock ?? false
readonly property bool numLock: keyboard?.numLock ?? false
readonly property string defaultKbLayout: keyboard?.layout.split(",")[0] ?? "??"
readonly property string kbLayoutFull: keyboard?.activeKeymap ?? "Unknown"
readonly property string kbLayout: kbMap.get(kbLayoutFull) ?? "??"
readonly property var kbMap: new Map()
readonly property alias extras: extras
readonly property alias options: extras.options
readonly property alias devices: extras.devices
property bool hadKeyboard
signal configReloaded
function getActiveScreen(): ShellScreen {
return Quickshell.screens.find(screen => root.monitorFor(screen) === root.focusedMonitor)
function dispatch(request: string): void {
Hyprland.dispatch(request);
}
function dispatch(request: string): void {
Hyprland.dispatch(request);
}
function getActiveScreen(): ShellScreen {
return Quickshell.screens.find(screen => root.monitorFor(screen) === root.focusedMonitor);
}
function monitorFor(screen: ShellScreen): HyprlandMonitor {
return Hyprland.monitorFor(screen);
}
function monitorFor(screen: ShellScreen): HyprlandMonitor {
return Hyprland.monitorFor(screen);
}
function reloadDynamicConfs(): void {
extras.batchMessage(["keyword bindlni ,Caps_Lock,global,zshell:refreshDevices", "keyword bindlni ,Num_Lock,global,zshell:refreshDevices"]);
}
function reloadDynamicConfs(): void {
extras.batchMessage(["keyword bindlni ,Caps_Lock,global,zshell:refreshDevices", "keyword bindlni ,Num_Lock,global,zshell:refreshDevices"]);
}
Component.onCompleted: reloadDynamicConfs()
Component.onCompleted: reloadDynamicConfs()
// function updateActiveWindow(): void {
// root.desktopName = root.applicationDir + root.activeToplevel?.lastIpcObject.class + ".desktop";
// }
// function updateActiveWindow(): void {
// root.desktopName = root.applicationDir + root.activeToplevel?.lastIpcObject.class + ".desktop";
// }
Connections {
target: Hyprland
Connections {
function onRawEvent(event: HyprlandEvent): void {
const n = event.name;
if (n.endsWith("v2"))
return;
function onRawEvent(event: HyprlandEvent): void {
const n = event.name;
if (n.endsWith("v2"))
return;
if (n === "configreloaded") {
root.configReloaded();
root.reloadDynamicConfs();
} else if (["workspace", "moveworkspace", "activespecial", "focusedmon"].includes(n)) {
Hyprland.refreshWorkspaces();
Hyprland.refreshMonitors();
// Qt.callLater( root.updateActiveWindow );
} else if (["openwindow", "closewindow", "movewindow"].includes(n)) {
Hyprland.refreshToplevels();
Hyprland.refreshWorkspaces();
// Qt.callLater( root.updateActiveWindow );
} else if (n.includes("mon")) {
Hyprland.refreshMonitors();
// Qt.callLater( root.updateActiveWindow );
} else if (n.includes("workspace")) {
Hyprland.refreshWorkspaces();
// Qt.callLater( root.updateActiveWindow );
} else if (n.includes("window") || n.includes("group") || ["pin", "fullscreen", "changefloatingmode", "minimize"].includes(n)) {
Hyprland.refreshToplevels();
// Qt.callLater( root.updateActiveWindow );
}
}
if (n === "configreloaded") {
root.configReloaded();
root.reloadDynamicConfs();
} else if (["workspace", "moveworkspace", "activespecial", "focusedmon"].includes(n)) {
Hyprland.refreshWorkspaces();
Hyprland.refreshMonitors();
// Qt.callLater( root.updateActiveWindow );
} else if (["openwindow", "closewindow", "movewindow"].includes(n)) {
Hyprland.refreshToplevels();
Hyprland.refreshWorkspaces();
// Qt.callLater( root.updateActiveWindow );
} else if (n.includes("mon")) {
Hyprland.refreshMonitors();
// Qt.callLater( root.updateActiveWindow );
} else if (n.includes("workspace")) {
Hyprland.refreshWorkspaces();
// Qt.callLater( root.updateActiveWindow );
} else if (n.includes("window") || n.includes("group") || ["pin", "fullscreen", "changefloatingmode", "minimize"].includes(n)) {
Hyprland.refreshToplevels();
// Qt.callLater( root.updateActiveWindow );
}
}
}
target: Hyprland
}
FileView {
id: desktopEntryName
FileView {
id: desktopEntryName
path: root.desktopName
path: root.desktopName
onLoaded: {
const lines = text().split( "\n" );
for ( const line of lines ) {
if ( line.startsWith( "Name=" )) {
let name = line.replace( "Name=", "" );
let caseFix = name[ 0 ].toUpperCase() + name.slice( 1 );
root.activeName = caseFix;
break;
}
}
}
}
onLoaded: {
const lines = text().split("\n");
for (const line of lines) {
if (line.startsWith("Name=")) {
let name = line.replace("Name=", "");
let caseFix = name[0].toUpperCase() + name.slice(1);
root.activeName = caseFix;
break;
}
}
}
}
FileView {
id: kbLayoutFile
FileView {
id: kbLayoutFile
path: Quickshell.env("ZSHELL_XKB_RULES_PATH") || "/usr/share/X11/xkb/rules/base.lst"
onLoaded: {
const layoutMatch = text().match(/! layout\n([\s\S]*?)\n\n/);
if (layoutMatch) {
const lines = layoutMatch[1].split("\n");
for (const line of lines) {
if (!line.trim() || line.trim().startsWith("!"))
continue;
path: Quickshell.env("ZSHELL_XKB_RULES_PATH") || "/usr/share/X11/xkb/rules/base.lst"
const match = line.match(/^\s*([a-z]{2,})\s+([a-zA-Z() ]+)$/);
if (match)
root.kbMap.set(match[2], match[1]);
}
}
onLoaded: {
const layoutMatch = text().match(/! layout\n([\s\S]*?)\n\n/);
if (layoutMatch) {
const lines = layoutMatch[1].split("\n");
for (const line of lines) {
if (!line.trim() || line.trim().startsWith("!"))
continue;
const variantMatch = text().match(/! variant\n([\s\S]*?)\n\n/);
if (variantMatch) {
const lines = variantMatch[1].split("\n");
for (const line of lines) {
if (!line.trim() || line.trim().startsWith("!"))
continue;
const match = line.match(/^\s*([a-z]{2,})\s+([a-zA-Z() ]+)$/);
if (match)
root.kbMap.set(match[2], match[1]);
}
}
const match = line.match(/^\s*([a-zA-Z0-9_-]+)\s+([a-z]{2,}): (.+)$/);
if (match)
root.kbMap.set(match[3], match[2]);
}
}
}
}
const variantMatch = text().match(/! variant\n([\s\S]*?)\n\n/);
if (variantMatch) {
const lines = variantMatch[1].split("\n");
for (const line of lines) {
if (!line.trim() || line.trim().startsWith("!"))
continue;
IpcHandler {
target: "hypr"
const match = line.match(/^\s*([a-zA-Z0-9_-]+)\s+([a-z]{2,}): (.+)$/);
if (match)
root.kbMap.set(match[3], match[2]);
}
}
}
}
function refreshDevices(): void {
extras.refreshDevices();
}
}
IpcHandler {
function refreshDevices(): void {
extras.refreshDevices();
}
CustomShortcut {
name: "refreshDevices"
onPressed: extras.refreshDevices()
onReleased: extras.refreshDevices()
}
target: "hypr"
}
HyprExtras {
id: extras
}
CustomShortcut {
name: "refreshDevices"
onPressed: extras.refreshDevices()
onReleased: extras.refreshDevices()
}
HyprExtras {
id: extras
}
}
+167 -168
View File
@@ -6,182 +6,181 @@ import Quickshell.Services.Notifications
import QtQuick
Singleton {
id: root
id: root
readonly property var weatherIcons: ({
"0": "clear_day",
"1": "clear_day",
"2": "partly_cloudy_day",
"3": "cloud",
"45": "foggy",
"48": "foggy",
"51": "rainy",
"53": "rainy",
"55": "rainy",
"56": "rainy",
"57": "rainy",
"61": "rainy",
"63": "rainy",
"65": "rainy",
"66": "rainy",
"67": "rainy",
"71": "cloudy_snowing",
"73": "cloudy_snowing",
"75": "snowing_heavy",
"77": "cloudy_snowing",
"80": "rainy",
"81": "rainy",
"82": "rainy",
"85": "cloudy_snowing",
"86": "snowing_heavy",
"95": "thunderstorm",
"96": "thunderstorm",
"99": "thunderstorm"
})
readonly property var categoryIcons: ({
WebBrowser: "web",
Printing: "print",
Security: "security",
Network: "chat",
Archiving: "archive",
Compression: "archive",
Development: "code",
IDE: "code",
TextEditor: "edit_note",
Audio: "music_note",
Music: "music_note",
Player: "music_note",
Recorder: "mic",
Game: "sports_esports",
FileTools: "files",
FileManager: "files",
Filesystem: "files",
FileTransfer: "files",
Settings: "settings",
DesktopSettings: "settings",
HardwareSettings: "settings",
TerminalEmulator: "terminal",
ConsoleOnly: "terminal",
Utility: "build",
Monitor: "monitor_heart",
Midi: "graphic_eq",
Mixer: "graphic_eq",
AudioVideoEditing: "video_settings",
AudioVideo: "music_video",
Video: "videocam",
Building: "construction",
Graphics: "photo_library",
"2DGraphics": "photo_library",
RasterGraphics: "photo_library",
TV: "tv",
System: "host",
Office: "content_paste"
})
readonly property var weatherIcons: ({
"0": "clear_day",
"1": "clear_day",
"2": "partly_cloudy_day",
"3": "cloud",
"45": "foggy",
"48": "foggy",
"51": "rainy",
"53": "rainy",
"55": "rainy",
"56": "rainy",
"57": "rainy",
"61": "rainy",
"63": "rainy",
"65": "rainy",
"66": "rainy",
"67": "rainy",
"71": "cloudy_snowing",
"73": "cloudy_snowing",
"75": "snowing_heavy",
"77": "cloudy_snowing",
"80": "rainy",
"81": "rainy",
"82": "rainy",
"85": "cloudy_snowing",
"86": "snowing_heavy",
"95": "thunderstorm",
"96": "thunderstorm",
"99": "thunderstorm"
})
readonly property var categoryIcons: ({
WebBrowser: "web",
Printing: "print",
Security: "security",
Network: "chat",
Archiving: "archive",
Compression: "archive",
Development: "code",
IDE: "code",
TextEditor: "edit_note",
Audio: "music_note",
Music: "music_note",
Player: "music_note",
Recorder: "mic",
Game: "sports_esports",
FileTools: "files",
FileManager: "files",
Filesystem: "files",
FileTransfer: "files",
Settings: "settings",
DesktopSettings: "settings",
HardwareSettings: "settings",
TerminalEmulator: "terminal",
ConsoleOnly: "terminal",
Utility: "build",
Monitor: "monitor_heart",
Midi: "graphic_eq",
Mixer: "graphic_eq",
AudioVideoEditing: "video_settings",
AudioVideo: "music_video",
Video: "videocam",
Building: "construction",
Graphics: "photo_library",
"2DGraphics": "photo_library",
RasterGraphics: "photo_library",
TV: "tv",
System: "host",
Office: "content_paste"
})
function getAppCategoryIcon(name: string, fallback: string): string {
const categories = DesktopEntries.heuristicLookup(name)?.categories;
function getAppIcon(name: string, fallback: string): string {
const icon = DesktopEntries.heuristicLookup(name)?.icon;
if (fallback !== "undefined")
return Quickshell.iconPath(icon, fallback);
return Quickshell.iconPath(icon);
}
if (categories)
for (const [key, value] of Object.entries(categoryIcons))
if (categories.includes(key))
return value;
return fallback;
}
function getAppCategoryIcon(name: string, fallback: string): string {
const categories = DesktopEntries.heuristicLookup(name)?.categories;
function getAppIcon(name: string, fallback: string): string {
const icon = DesktopEntries.heuristicLookup(name)?.icon;
if (fallback !== "undefined")
return Quickshell.iconPath(icon, fallback);
return Quickshell.iconPath(icon);
}
if (categories)
for (const [key, value] of Object.entries(categoryIcons))
if (categories.includes(key))
return value;
return fallback;
}
function getBluetoothIcon(icon: string): string {
if (icon.includes("headset") || icon.includes("headphones"))
return "headphones";
if (icon.includes("audio"))
return "speaker";
if (icon.includes("phone"))
return "smartphone";
if (icon.includes("mouse"))
return "mouse";
if (icon.includes("keyboard"))
return "keyboard";
return "bluetooth";
}
function getNetworkIcon(strength: int, isSecure = false): string {
if (isSecure) {
if (strength >= 80)
return "network_wifi_locked";
if (strength >= 60)
return "network_wifi_3_bar_locked";
if (strength >= 40)
return "network_wifi_2_bar_locked";
if (strength >= 20)
return "network_wifi_1_bar_locked";
return "signal_wifi_0_bar";
} else {
if (strength >= 80)
return "network_wifi";
if (strength >= 60)
return "network_wifi_3_bar";
if (strength >= 40)
return "network_wifi_2_bar";
if (strength >= 20)
return "network_wifi_1_bar";
return "signal_wifi_0_bar";
}
}
function getMicVolumeIcon(volume: real, isMuted: bool): string {
if (!isMuted && volume > 0)
return "mic";
return "mic_off";
}
function getBluetoothIcon(icon: string): string {
if (icon.includes("headset") || icon.includes("headphones"))
return "headphones";
if (icon.includes("audio"))
return "speaker";
if (icon.includes("phone"))
return "smartphone";
if (icon.includes("mouse"))
return "mouse";
if (icon.includes("keyboard"))
return "keyboard";
return "bluetooth";
}
function getNetworkIcon(strength: int, isSecure = false): string {
if (isSecure) {
if (strength >= 80)
return "network_wifi_locked";
if (strength >= 60)
return "network_wifi_3_bar_locked";
if (strength >= 40)
return "network_wifi_2_bar_locked";
if (strength >= 20)
return "network_wifi_1_bar_locked";
return "signal_wifi_0_bar";
} else {
if (strength >= 80)
return "network_wifi";
if (strength >= 60)
return "network_wifi_3_bar";
if (strength >= 40)
return "network_wifi_2_bar";
if (strength >= 20)
return "network_wifi_1_bar";
return "signal_wifi_0_bar";
}
}
function getWeatherIcon(code: string): string {
if (weatherIcons.hasOwnProperty(code))
return weatherIcons[code];
return "air";
}
function getNotifIcon(summary: string, urgency: int): string {
summary = summary.toLowerCase();
if (summary.includes("reboot"))
return "restart_alt";
if (summary.includes("recording"))
return "screen_record";
if (summary.includes("battery"))
return "power";
if (summary.includes("screenshot"))
return "screenshot_monitor";
if (summary.includes("welcome"))
return "waving_hand";
if (summary.includes("time") || summary.includes("a break"))
return "schedule";
if (summary.includes("installed"))
return "download";
if (summary.includes("update"))
return "update";
if (summary.includes("unable to"))
return "deployed_code_alert";
if (summary.includes("profile"))
return "person";
if (summary.includes("file"))
return "folder_copy";
if (urgency === NotificationUrgency.Critical)
return "release_alert";
return "chat";
}
function getNotifIcon(summary: string, urgency: int): string {
summary = summary.toLowerCase();
if (summary.includes("reboot"))
return "restart_alt";
if (summary.includes("recording"))
return "screen_record";
if (summary.includes("battery"))
return "power";
if (summary.includes("screenshot"))
return "screenshot_monitor";
if (summary.includes("welcome"))
return "waving_hand";
if (summary.includes("time") || summary.includes("a break"))
return "schedule";
if (summary.includes("installed"))
return "download";
if (summary.includes("update"))
return "update";
if (summary.includes("unable to"))
return "deployed_code_alert";
if (summary.includes("profile"))
return "person";
if (summary.includes("file"))
return "folder_copy";
if (urgency === NotificationUrgency.Critical)
return "release_alert";
return "chat";
}
function getVolumeIcon(volume: real, isMuted: bool): string {
if (isMuted)
return "no_sound";
if (volume >= 0.5)
return "volume_up";
if (volume > 0)
return "volume_down";
return "volume_mute";
}
function getVolumeIcon(volume: real, isMuted: bool): string {
if (isMuted)
return "no_sound";
if (volume >= 0.5)
return "volume_up";
if (volume > 0)
return "volume_down";
return "volume_mute";
}
function getMicVolumeIcon(volume: real, isMuted: bool): string {
if (!isMuted && volume > 0)
return "mic";
return "mic_off";
}
function getWeatherIcon(code: string): string {
if (weatherIcons.hasOwnProperty(code))
return weatherIcons[code];
return "air";
}
}
+40 -37
View File
@@ -5,52 +5,55 @@ import Quickshell.Io
import Quickshell.Wayland
Singleton {
id: root
id: root
property alias enabled: props.enabled
readonly property alias enabledSince: props.enabledSince
property alias enabled: props.enabled
readonly property alias enabledSince: props.enabledSince
onEnabledChanged: {
if (enabled)
props.enabledSince = new Date();
}
onEnabledChanged: {
if (enabled)
props.enabledSince = new Date();
}
PersistentProperties {
id: props
PersistentProperties {
id: props
property bool enabled
property date enabledSince
property bool enabled
property date enabledSince
reloadableId: "idleInhibitor"
}
reloadableId: "idleInhibitor"
}
IdleInhibitor {
enabled: props.enabled
window: PanelWindow {
implicitWidth: 0
implicitHeight: 0
color: "transparent"
mask: Region {}
}
}
IdleInhibitor {
enabled: props.enabled
IpcHandler {
target: "idleInhibitor"
window: PanelWindow {
color: "transparent"
implicitHeight: 0
implicitWidth: 0
function isEnabled(): bool {
return props.enabled;
}
mask: Region {
}
}
}
function toggle(): void {
props.enabled = !props.enabled;
}
IpcHandler {
function disable(): void {
props.enabled = false;
}
function enable(): void {
props.enabled = true;
}
function enable(): void {
props.enabled = true;
}
function disable(): void {
props.enabled = false;
}
}
function isEnabled(): bool {
return props.enabled;
}
function toggle(): void {
props.enabled = !props.enabled;
}
target: "idleInhibitor"
}
}
+8 -8
View File
@@ -5,14 +5,14 @@ import Quickshell.Hyprland
import qs.Helpers
Singleton {
function getInitialTitle(callback) {
let activeWindow = Hypr.activeToplevel.title
let activeClass = Hypr.activeToplevel.lastIpcObject.class.toString()
let regex = new RegExp(activeClass, "i")
function getInitialTitle(callback) {
let activeWindow = Hypr.activeToplevel.title;
let activeClass = Hypr.activeToplevel.lastIpcObject.class.toString();
let regex = new RegExp(activeClass, "i");
console.log("ActiveWindow", activeWindow, "ActiveClass", activeClass, "Regex", regex)
console.log("ActiveWindow", activeWindow, "ActiveClass", activeClass, "Regex", regex);
const evalTitle = activeWindow.match(regex)
callback(evalTitle)
}
const evalTitle = activeWindow.match(regex);
callback(evalTitle);
}
}
+35 -35
View File
@@ -10,32 +10,11 @@ import qs.Paths
Singleton {
id: root
readonly property int darkStart: Config.general.color.scheduleDarkStart
readonly property int darkEnd: Config.general.color.scheduleDarkEnd
Timer {
id: darkModeTimer
interval: 5000
running: true
repeat: true
onTriggered: {
if ( darkStart === darkEnd )
return;
var now = new Date();
if ( now.getHours() >= darkStart || now.getHours() < darkEnd ) {
if ( DynamicColors.light )
applyDarkMode();
} else {
if ( !DynamicColors.light )
applyLightMode();
}
}
}
readonly property int darkStart: Config.general.color.scheduleDarkStart
function applyDarkMode() {
if ( Config.general.color.schemeGeneration ) {
if (Config.general.color.schemeGeneration) {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${WallpaperPath.currentWallpaperPath}`, "--thumbnail-path", `${Paths.cache}/imagecache/thumbnail.jpg`, "--output", `${Paths.state}/scheme.json`, "--scheme", `${Config.colors.schemeType}`, "--mode", "dark"]);
} else {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--preset", `${DynamicColors.scheme}:${DynamicColors.flavour}`, "--output", `${Paths.state}/scheme.json`, "--mode", "dark"]);
@@ -43,18 +22,18 @@ Singleton {
Config.general.color.mode = "dark";
Quickshell.execDetached(["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", "'prefer-dark'"])
Quickshell.execDetached(["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", "'prefer-dark'"]);
Quickshell.execDetached(["sh", "-c", `sed -i 's/color_scheme_path=\\(.*\\)Light.colors/color_scheme_path=\\1Dark.colors/' ${Paths.home}/.config/qt6ct/qt6ct.conf`])
Quickshell.execDetached(["sh", "-c", `sed -i 's/color_scheme_path=\\(.*\\)Light.colors/color_scheme_path=\\1Dark.colors/' ${Paths.home}/.config/qt6ct/qt6ct.conf`]);
Quickshell.execDetached(["sed", "-i", "'s/\\(vim.cmd.colorscheme \\).*/\\1\"tokyodark\"/'", "~/.config/nvim/lua/config/load-colorscheme.lua"])
Quickshell.execDetached(["sed", "-i", "'s/\\(vim.cmd.colorscheme \\).*/\\1\"tokyodark\"/'", "~/.config/nvim/lua/config/load-colorscheme.lua"]);
if( Config.general.color.wallust )
if (Config.general.color.wallust)
Wallust.generateColors(WallpaperPath.currentWallpaperPath);
}
function applyLightMode() {
if ( Config.general.color.neovimColors ) {
if (Config.general.color.neovimColors) {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${WallpaperPath.currentWallpaperPath}`, "--thumbnail-path", `${Paths.cache}/imagecache/thumbnail.jpg`, "--output", `${Paths.state}/scheme.json`, "--scheme", `${Config.colors.schemeType}`, "--mode", "light"]);
} else {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--preset", `${DynamicColors.scheme}:${DynamicColors.flavour}`, "--output", `${Paths.state}/scheme.json`, "--mode", "light"]);
@@ -62,25 +41,46 @@ Singleton {
Config.general.color.mode = "light";
Quickshell.execDetached(["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", "'prefer-light'"])
Quickshell.execDetached(["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", "'prefer-light'"]);
Quickshell.execDetached(["sh", "-c", `sed -i 's/color_scheme_path=\\(.*\\)Dark.colors/color_scheme_path=\\1Light.colors/' ${Paths.home}/.config/qt6ct/qt6ct.conf`])
Quickshell.execDetached(["sh", "-c", `sed -i 's/color_scheme_path=\\(.*\\)Dark.colors/color_scheme_path=\\1Light.colors/' ${Paths.home}/.config/qt6ct/qt6ct.conf`]);
if ( Config.general.color.neovimColors )
Quickshell.execDetached(["sed", "-i", "'s/\\(vim.cmd.colorscheme \\).*/\\1\"onelight\"/'", "~/.config/nvim/lua/config/load-colorscheme.lua"])
if (Config.general.color.neovimColors)
Quickshell.execDetached(["sed", "-i", "'s/\\(vim.cmd.colorscheme \\).*/\\1\"onelight\"/'", "~/.config/nvim/lua/config/load-colorscheme.lua"]);
if( Config.general.color.wallust )
if (Config.general.color.wallust)
Wallust.generateColors(WallpaperPath.currentWallpaperPath);
}
function checkStartup() {
if ( darkStart === darkEnd )
if (darkStart === darkEnd)
return;
var now = new Date();
if ( now.getHours() >= darkStart || now.getHours() < darkEnd ) {
if (now.getHours() >= darkStart || now.getHours() < darkEnd) {
applyDarkMode();
} else {
applyLightMode();
}
}
Timer {
id: darkModeTimer
interval: 5000
repeat: true
running: true
onTriggered: {
if (darkStart === darkEnd)
return;
var now = new Date();
if (now.getHours() >= darkStart || now.getHours() < darkEnd) {
if (DynamicColors.light)
applyDarkMode();
} else {
if (!DynamicColors.light)
applyLightMode();
}
}
}
}
+1 -1
View File
@@ -6,6 +6,6 @@ import Quickshell.Networking
Singleton {
id: root
property list<NetworkDevice> devices: Networking.devices.values
property NetworkDevice activeDevice: devices.find(d => d.connected)
property list<NetworkDevice> devices: Networking.devices.values
}
+6 -6
View File
@@ -4,13 +4,13 @@ import Quickshell
import Quickshell.Io
Singleton {
id: root
id: root
property alias centerX: notifCenterSpacing.centerX
property alias centerX: notifCenterSpacing.centerX
JsonAdapter {
id: notifCenterSpacing
JsonAdapter {
id: notifCenterSpacing
property int centerX
}
property int centerX
}
}
+6 -6
View File
@@ -4,13 +4,13 @@ import Quickshell.Io
import Quickshell
Singleton {
id: root
id: root
property alias notifPath: storage.notifPath
property alias notifPath: storage.notifPath
JsonAdapter {
id: storage
JsonAdapter {
id: storage
property string notifPath: Quickshell.statePath("notifications.json")
}
property string notifPath: Quickshell.statePath("notifications.json")
}
}
+234 -243
View File
@@ -10,286 +10,277 @@ import qs.Config
import qs.Helpers
MouseArea {
id: root
id: root
required property LazyLoader loader
required property ShellScreen screen
property list<var> clients: {
const mon = Hypr.monitorFor(screen);
if (!mon)
return [];
property bool onClient
const special = mon.lastIpcObject.specialWorkspace;
const wsId = special.name ? special.id : mon.activeWorkspace.id;
property real realBorderWidth: onClient ? (Hypr.options["general:border_size"] ?? 1) : 2
property real realRounding: onClient ? (Hypr.options["decoration:rounding"] ?? 0) : 0
return Hypr.toplevels.values.filter(c => c.workspace?.id === wsId).sort((a, b) => {
const ac = a.lastIpcObject;
const bc = b.lastIpcObject;
return (bc.pinned - ac.pinned) || ((bc.fullscreen !== 0) - (ac.fullscreen !== 0)) || (bc.floating - ac.floating);
});
}
property real ex: screen.width
property real ey: screen.height
required property LazyLoader loader
property bool onClient
property real realBorderWidth: onClient ? (Hypr.options["general:border_size"] ?? 1) : 2
property real realRounding: onClient ? (Hypr.options["decoration:rounding"] ?? 0) : 0
property real rsx: Math.min(sx, ex)
property real rsy: Math.min(sy, ey)
required property ShellScreen screen
property real sh: Math.abs(sy - ey)
property real ssx
property real ssy
property real sw: Math.abs(sx - ex)
property real sx: 0
property real sy: 0
property real ssx
property real ssy
function checkClientRects(x: real, y: real): void {
for (const client of clients) {
if (!client)
continue;
property real sx: 0
property real sy: 0
property real ex: screen.width
property real ey: screen.height
let {
at: [cx, cy],
size: [cw, ch]
} = client.lastIpcObject;
cx -= screen.x;
cy -= screen.y;
if (cx <= x && cy <= y && cx + cw >= x && cy + ch >= y) {
onClient = true;
sx = cx;
sy = cy;
ex = cx + cw;
ey = cy + ch;
break;
}
}
}
property real rsx: Math.min(sx, ex)
property real rsy: Math.min(sy, ey)
property real sw: Math.abs(sx - ex)
property real sh: Math.abs(sy - ey)
function save(): void {
const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`);
ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached(["swappy", "-f", path]));
closeAnim.start();
}
property list<var> clients: {
const mon = Hypr.monitorFor(screen);
if (!mon)
return [];
anchors.fill: parent
cursorShape: Qt.CrossCursor
focus: true
hoverEnabled: true
opacity: 0
const special = mon.lastIpcObject.specialWorkspace;
const wsId = special.name ? special.id : mon.activeWorkspace.id;
Behavior on opacity {
Anim {
duration: 300
}
}
Behavior on rsx {
enabled: !root.pressed
return Hypr.toplevels.values.filter(c => c.workspace?.id === wsId).sort((a, b) => {
const ac = a.lastIpcObject;
const bc = b.lastIpcObject;
return (bc.pinned - ac.pinned) || ((bc.fullscreen !== 0) - (ac.fullscreen !== 0)) || (bc.floating - ac.floating);
});
}
ExAnim {
}
}
Behavior on rsy {
enabled: !root.pressed
function checkClientRects(x: real, y: real): void {
for (const client of clients) {
if (!client)
continue;
ExAnim {
}
}
Behavior on sh {
enabled: !root.pressed
let {
at: [cx, cy],
size: [cw, ch]
} = client.lastIpcObject;
cx -= screen.x;
cy -= screen.y;
if (cx <= x && cy <= y && cx + cw >= x && cy + ch >= y) {
onClient = true;
sx = cx;
sy = cy;
ex = cx + cw;
ey = cy + ch;
break;
}
}
}
ExAnim {
}
}
Behavior on sw {
enabled: !root.pressed
function save(): void {
const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`);
ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached(["swappy", "-f", path]));
closeAnim.start();
}
ExAnim {
}
}
onClientsChanged: checkClientRects(mouseX, mouseY)
Component.onCompleted: {
Hypr.extras.refreshOptions();
if (loader.freeze)
clients = clients;
anchors.fill: parent
opacity: 0
hoverEnabled: true
cursorShape: Qt.CrossCursor
opacity = 1;
Component.onCompleted: {
Hypr.extras.refreshOptions();
if (loader.freeze)
clients = clients;
const c = clients[0];
if (c) {
const cx = c.lastIpcObject.at[0] - screen.x;
const cy = c.lastIpcObject.at[1] - screen.y;
onClient = true;
sx = cx;
sy = cy;
ex = cx + c.lastIpcObject.size[0];
ey = cy + c.lastIpcObject.size[1];
} else {
sx = screen.width / 2 - 100;
sy = screen.height / 2 - 100;
ex = screen.width / 2 + 100;
ey = screen.height / 2 + 100;
}
}
Keys.onEscapePressed: closeAnim.start()
onClientsChanged: checkClientRects(mouseX, mouseY)
onPositionChanged: event => {
const x = event.x;
const y = event.y;
opacity = 1;
if (pressed) {
onClient = false;
sx = ssx;
sy = ssy;
ex = x;
ey = y;
} else {
checkClientRects(x, y);
}
}
onPressed: event => {
ssx = event.x;
ssy = event.y;
}
onReleased: {
if (closeAnim.running)
return;
const c = clients[0];
if (c) {
const cx = c.lastIpcObject.at[0] - screen.x;
const cy = c.lastIpcObject.at[1] - screen.y;
onClient = true;
sx = cx;
sy = cy;
ex = cx + c.lastIpcObject.size[0];
ey = cy + c.lastIpcObject.size[1];
} else {
sx = screen.width / 2 - 100;
sy = screen.height / 2 - 100;
ex = screen.width / 2 + 100;
ey = screen.height / 2 + 100;
}
}
if (root.loader.freeze) {
save();
} else {
overlay.visible = border.visible = false;
screencopy.visible = false;
screencopy.active = true;
}
}
onPressed: event => {
ssx = event.x;
ssy = event.y;
}
SequentialAnimation {
id: closeAnim
onReleased: {
if (closeAnim.running)
return;
PropertyAction {
property: "closing"
target: root.loader
value: true
}
if (root.loader.freeze) {
save();
} else {
overlay.visible = border.visible = false;
screencopy.visible = false;
screencopy.active = true;
}
}
ParallelAnimation {
Anim {
duration: 300
property: "opacity"
target: root
to: 0
}
onPositionChanged: event => {
const x = event.x;
const y = event.y;
ExAnim {
properties: "rsx,rsy"
target: root
to: 0
}
if (pressed) {
onClient = false;
sx = ssx;
sy = ssy;
ex = x;
ey = y;
} else {
checkClientRects(x, y);
}
}
ExAnim {
property: "sw"
target: root
to: root.screen.width
}
focus: true
Keys.onEscapePressed: closeAnim.start()
ExAnim {
property: "sh"
target: root
to: root.screen.height
}
}
SequentialAnimation {
id: closeAnim
PropertyAction {
property: "activeAsync"
target: root.loader
value: false
}
}
PropertyAction {
target: root.loader
property: "closing"
value: true
}
ParallelAnimation {
Anim {
target: root
property: "opacity"
to: 0
duration: 300
}
ExAnim {
target: root
properties: "rsx,rsy"
to: 0
}
ExAnim {
target: root
property: "sw"
to: root.screen.width
}
ExAnim {
target: root
property: "sh"
to: root.screen.height
}
}
PropertyAction {
target: root.loader
property: "activeAsync"
value: false
}
}
Loader {
id: screencopy
Loader {
id: screencopy
active: root.loader.freeze
anchors.fill: parent
asynchronous: true
anchors.fill: parent
sourceComponent: ScreencopyView {
captureSource: root.screen
paintCursor: false
active: root.loader.freeze
asynchronous: true
onHasContentChanged: {
if (hasContent && !root.loader.freeze) {
overlay.visible = border.visible = true;
root.save();
}
}
}
}
sourceComponent: ScreencopyView {
captureSource: root.screen
Rectangle {
id: overlay
paintCursor: false
anchors.fill: parent
color: "white"
layer.enabled: true
opacity: 0.3
radius: root.realRounding
onHasContentChanged: {
if (hasContent && !root.loader.freeze) {
overlay.visible = border.visible = true;
root.save();
}
}
}
}
layer.effect: MultiEffect {
maskEnabled: true
maskInverted: true
maskSource: selectionWrapper
maskSpreadAtMin: 1
maskThresholdMin: 0.5
}
}
Rectangle {
id: overlay
Item {
id: selectionWrapper
anchors.fill: parent
color: "white"
opacity: 0.3
anchors.fill: parent
layer.enabled: true
visible: false
radius: root.realRounding
Rectangle {
id: selectionRect
layer.enabled: true
layer.effect: MultiEffect {
maskSource: selectionWrapper
maskEnabled: true
maskInverted: true
maskSpreadAtMin: 1
maskThresholdMin: 0.5
}
}
implicitHeight: root.sh
implicitWidth: root.sw
radius: root.realRounding
x: root.rsx
y: root.rsy
}
}
Item {
id: selectionWrapper
Rectangle {
id: border
anchors.fill: parent
layer.enabled: true
visible: false
border.color: DynamicColors.palette.m3primary
border.width: root.realBorderWidth
color: "transparent"
implicitHeight: selectionRect.implicitHeight + root.realBorderWidth * 2
implicitWidth: selectionRect.implicitWidth + root.realBorderWidth * 2
radius: root.realRounding > 0 ? root.realRounding + root.realBorderWidth : 0
x: selectionRect.x - root.realBorderWidth
y: selectionRect.y - root.realBorderWidth
Rectangle {
id: selectionRect
Behavior on border.color {
Anim {
}
}
}
radius: root.realRounding
x: root.rsx
y: root.rsy
implicitWidth: root.sw
implicitHeight: root.sh
}
}
Rectangle {
id: border
color: "transparent"
radius: root.realRounding > 0 ? root.realRounding + root.realBorderWidth : 0
border.width: root.realBorderWidth
border.color: DynamicColors.palette.m3primary
x: selectionRect.x - root.realBorderWidth
y: selectionRect.y - root.realBorderWidth
implicitWidth: selectionRect.implicitWidth + root.realBorderWidth * 2
implicitHeight: selectionRect.implicitHeight + root.realBorderWidth * 2
Behavior on border.color {
Anim {}
}
}
Behavior on opacity {
Anim {
duration: 300
}
}
Behavior on rsx {
enabled: !root.pressed
ExAnim {}
}
Behavior on rsy {
enabled: !root.pressed
ExAnim {}
}
Behavior on sw {
enabled: !root.pressed
ExAnim {}
}
Behavior on sh {
enabled: !root.pressed
ExAnim {}
}
component ExAnim: Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
component ExAnim: Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
+95 -91
View File
@@ -9,115 +9,119 @@ import qs.Config
import qs.Components
Singleton {
id: root
id: root
readonly property list<MprisPlayer> list: Mpris.players.values
readonly property MprisPlayer active: props.manualActive ?? list.find(p => getIdentity(p) === Config.services.defaultPlayer) ?? list[0] ?? null
property alias manualActive: props.manualActive
readonly property MprisPlayer active: props.manualActive ?? list.find(p => getIdentity(p) === Config.services.defaultPlayer) ?? list[0] ?? null
readonly property list<MprisPlayer> list: Mpris.players.values
property alias manualActive: props.manualActive
function getIdentity(player: MprisPlayer): string {
const alias = Config.services.playerAliases.find(a => a.from === player.identity);
return alias?.to ?? player.identity;
}
function getIdentity(player: MprisPlayer): string {
const alias = Config.services.playerAliases.find(a => a.from === player.identity);
return alias?.to ?? player.identity;
}
Connections {
target: active
Connections {
function onPostTrackChanged() {
if (!Config.utilities.toasts.nowPlaying) {
return;
}
}
function onPostTrackChanged() {
if (!Config.utilities.toasts.nowPlaying) {
return;
}
}
}
target: active
}
PersistentProperties {
id: props
PersistentProperties {
id: props
property MprisPlayer manualActive
property MprisPlayer manualActive
reloadableId: "players"
}
reloadableId: "players"
}
CustomShortcut {
name: "mediaToggle"
description: "Toggle media playback"
onPressed: {
const active = root.active;
if (active && active.canTogglePlaying)
active.togglePlaying();
}
}
CustomShortcut {
description: "Toggle media playback"
name: "mediaToggle"
CustomShortcut {
name: "mediaPrev"
description: "Previous track"
onPressed: {
const active = root.active;
if (active && active.canGoPrevious)
active.previous();
}
}
onPressed: {
const active = root.active;
if (active && active.canTogglePlaying)
active.togglePlaying();
}
}
CustomShortcut {
name: "mediaNext"
description: "Next track"
onPressed: {
const active = root.active;
if (active && active.canGoNext)
active.next();
}
}
CustomShortcut {
description: "Previous track"
name: "mediaPrev"
CustomShortcut {
name: "mediaStop"
description: "Stop media playback"
onPressed: root.active?.stop()
}
onPressed: {
const active = root.active;
if (active && active.canGoPrevious)
active.previous();
}
}
IpcHandler {
target: "mpris"
CustomShortcut {
description: "Next track"
name: "mediaNext"
function getActive(prop: string): string {
const active = root.active;
return active ? active[prop] ?? "Invalid property" : "No active player";
}
onPressed: {
const active = root.active;
if (active && active.canGoNext)
active.next();
}
}
function list(): string {
return root.list.map(p => root.getIdentity(p)).join("\n");
}
CustomShortcut {
description: "Stop media playback"
name: "mediaStop"
function play(): void {
const active = root.active;
if (active?.canPlay)
active.play();
}
onPressed: root.active?.stop()
}
function pause(): void {
const active = root.active;
if (active?.canPause)
active.pause();
}
IpcHandler {
function getActive(prop: string): string {
const active = root.active;
return active ? active[prop] ?? "Invalid property" : "No active player";
}
function playPause(): void {
const active = root.active;
if (active?.canTogglePlaying)
active.togglePlaying();
}
function list(): string {
return root.list.map(p => root.getIdentity(p)).join("\n");
}
function previous(): void {
const active = root.active;
if (active?.canGoPrevious)
active.previous();
}
function next(): void {
const active = root.active;
if (active?.canGoNext)
active.next();
}
function next(): void {
const active = root.active;
if (active?.canGoNext)
active.next();
}
function pause(): void {
const active = root.active;
if (active?.canPause)
active.pause();
}
function stop(): void {
root.active?.stop();
}
}
function play(): void {
const active = root.active;
if (active?.canPlay)
active.play();
}
function playPause(): void {
const active = root.active;
if (active?.canTogglePlaying)
active.togglePlaying();
}
function previous(): void {
const active = root.active;
if (active?.canGoPrevious)
active.previous();
}
function stop(): void {
root.active?.stop();
}
target: "mpris"
}
}
+30 -30
View File
@@ -9,41 +9,41 @@ import qs.Helpers
import qs.Paths
Searcher {
id: root
id: root
property bool showPreview: false
readonly property string current: showPreview ? previewPath : actualCurrent
property string previewPath
property string actualCurrent: WallpaperPath.currentWallpaperPath
property string actualCurrent: WallpaperPath.currentWallpaperPath
readonly property string current: showPreview ? previewPath : actualCurrent
property string previewPath
property bool showPreview: false
function setWallpaper(path: string): void {
actualCurrent = path;
WallpaperPath.currentWallpaperPath = path;
function preview(path: string): void {
previewPath = path;
showPreview = true;
}
function setWallpaper(path: string): void {
actualCurrent = path;
WallpaperPath.currentWallpaperPath = path;
Quickshell.execDetached(["sh", "-c", `python3 ${Quickshell.shellPath("scripts/LockScreenBg.py")} --input_image=${root.actualCurrent} --output_path=${Paths.state}/lockscreen_bg.png`]);
}
}
function preview(path: string): void {
previewPath = path;
showPreview = true;
}
function stopPreview(): void {
showPreview = false;
Quickshell.execDetached(["sh", "-c", `python3 ${Quickshell.shellPath("scripts/SchemeColorGen.py")} --path=${root.actualCurrent} --thumbnail=${Paths.cache}/imagecache/thumbnail.jpg --output=${Paths.state}/scheme.json --scheme=${Config.colors.schemeType}`]);
}
function stopPreview(): void {
showPreview = false;
Quickshell.execDetached(["sh", "-c", `python3 ${Quickshell.shellPath("scripts/SchemeColorGen.py")} --path=${root.actualCurrent} --thumbnail=${Paths.cache}/imagecache/thumbnail.jpg --output=${Paths.state}/scheme.json --scheme=${Config.colors.schemeType}`]);
}
extraOpts: useFuzzy ? ({}) : ({
forward: false
})
key: "relativePath"
list: wallpapers.entries
useFuzzy: true
list: wallpapers.entries
key: "relativePath"
useFuzzy: true
extraOpts: useFuzzy ? ({}) : ({
forward: false
})
FileSystemModel {
id: wallpapers
FileSystemModel {
id: wallpapers
recursive: true
path: Config.general.wallpaperPath
filter: FileSystemModel.Images
}
filter: FileSystemModel.Images
path: Config.general.wallpaperPath
recursive: true
}
}
+41 -42
View File
@@ -4,52 +4,51 @@ import "../scripts/fuzzysort.js" as Fuzzy
import QtQuick
Singleton {
required property list<QtObject> list
property string key: "name"
property bool useFuzzy: false
property var extraOpts: ({})
property var extraOpts: ({})
readonly property list<var> fuzzyPrepped: useFuzzy ? list.map(e => {
const obj = {
_item: e
};
for (const k of keys)
obj[k] = Fuzzy.prepare(e[k]);
return obj;
}) : []
readonly property var fzf: useFuzzy ? [] : new Fzf.Finder(list, Object.assign({
selector
}, extraOpts))
property string key: "name"
// Extra stuff for fuzzy
property list<string> keys: [key]
property list<real> weights: [1]
// Extra stuff for fuzzy
property list<string> keys: [key]
required property list<QtObject> list
property bool useFuzzy: false
property list<real> weights: [1]
readonly property var fzf: useFuzzy ? [] : new Fzf.Finder(list, Object.assign({
selector
}, extraOpts))
readonly property list<var> fuzzyPrepped: useFuzzy ? list.map(e => {
const obj = {
_item: e
};
for (const k of keys)
obj[k] = Fuzzy.prepare(e[k]);
return obj;
}) : []
function query(search: string): list<var> {
search = transformSearch(search);
if (!search)
return [...list];
function transformSearch(search: string): string {
return search;
}
if (useFuzzy)
return Fuzzy.go(search, fuzzyPrepped, Object.assign({
all: true,
keys,
scoreFn: r => weights.reduce((a, w, i) => a + r[i].score * w, 0)
}, extraOpts)).map(r => r.obj._item);
function selector(item: var): string {
// Only for fzf
return item[key];
}
return fzf.find(search).sort((a, b) => {
if (a.score === b.score)
return selector(a.item).trim().length - selector(b.item).trim().length;
return b.score - a.score;
}).map(r => r.item);
}
function query(search: string): list<var> {
search = transformSearch(search);
if (!search)
return [...list];
function selector(item: var): string {
// Only for fzf
return item[key];
}
if (useFuzzy)
return Fuzzy.go(search, fuzzyPrepped, Object.assign({
all: true,
keys,
scoreFn: r => weights.reduce((a, w, i) => a + r[i].score * w, 0)
}, extraOpts)).map(r => r.obj._item);
return fzf.find(search).sort((a, b) => {
if (a.score === b.score)
return selector(a.item).trim().length - selector(b.item).trim().length;
return b.score - a.score;
}).map(r => r.item);
}
function transformSearch(search: string): string {
return search;
}
}
+63 -61
View File
@@ -6,79 +6,81 @@ import Quickshell.Io
import QtQuick
Singleton {
id: root
id: root
property string osName
property string osPrettyName
property string osId
property list<string> osIdLike
property string osLogo
property bool isDefaultLogo: true
property bool isDefaultLogo: true
property string osId
property list<string> osIdLike
property string osLogo
property string osName
property string osPrettyName
readonly property string shell: Quickshell.env("SHELL").split("/").pop()
property string uptime
readonly property string user: Quickshell.env("USER")
readonly property string wm: Quickshell.env("XDG_CURRENT_DESKTOP") || Quickshell.env("XDG_SESSION_DESKTOP")
property string uptime
readonly property string user: Quickshell.env("USER")
readonly property string wm: Quickshell.env("XDG_CURRENT_DESKTOP") || Quickshell.env("XDG_SESSION_DESKTOP")
readonly property string shell: Quickshell.env("SHELL").split("/").pop()
FileView {
id: osRelease
FileView {
id: osRelease
path: "/etc/os-release"
path: "/etc/os-release"
onLoaded: {
const lines = text().split("\n");
onLoaded: {
const lines = text().split("\n");
const fd = key => lines.find(l => l.startsWith(`${key}=`))?.split("=")[1].replace(/"/g, "") ?? "";
const fd = key => lines.find(l => l.startsWith(`${key}=`))?.split("=")[1].replace(/"/g, "") ?? "";
root.osName = fd("NAME");
root.osPrettyName = fd("PRETTY_NAME");
root.osId = fd("ID");
root.osIdLike = fd("ID_LIKE").split(" ");
root.osName = fd("NAME");
root.osPrettyName = fd("PRETTY_NAME");
root.osId = fd("ID");
root.osIdLike = fd("ID_LIKE").split(" ");
const logo = Quickshell.iconPath(fd("LOGO"), true);
if (Config.general.logo) {
root.osLogo = Quickshell.iconPath(Config.general.logo, true) || "file://" + Paths.absolutePath(Config.general.logo);
root.isDefaultLogo = false;
} else if (logo) {
root.osLogo = logo;
root.isDefaultLogo = false;
}
}
}
const logo = Quickshell.iconPath(fd("LOGO"), true);
if (Config.general.logo) {
root.osLogo = Quickshell.iconPath(Config.general.logo, true) || "file://" + Paths.absolutePath(Config.general.logo);
root.isDefaultLogo = false;
} else if (logo) {
root.osLogo = logo;
root.isDefaultLogo = false;
}
}
}
Connections {
target: Config.general
Connections {
function onLogoChanged(): void {
osRelease.reload();
}
function onLogoChanged(): void {
osRelease.reload();
}
}
target: Config.general
}
Timer {
running: true
repeat: true
interval: 15000
onTriggered: fileUptime.reload()
}
Timer {
interval: 15000
repeat: true
running: true
FileView {
id: fileUptime
onTriggered: fileUptime.reload()
}
path: "/proc/uptime"
onLoaded: {
const up = parseInt(text().split(" ")[0] ?? 0);
FileView {
id: fileUptime
const days = Math.floor(up / 86400);
const hours = Math.floor((up % 86400) / 3600);
const minutes = Math.floor((up % 3600) / 60);
path: "/proc/uptime"
let str = "";
if (days > 0)
str += `${days} day${days === 1 ? "" : "s"}`;
if (hours > 0)
str += `${str ? ", " : ""}${hours} hour${hours === 1 ? "" : "s"}`;
if (minutes > 0 || !str)
str += `${str ? ", " : ""}${minutes} minute${minutes === 1 ? "" : "s"}`;
root.uptime = str;
}
}
onLoaded: {
const up = parseInt(text().split(" ")[0] ?? 0);
const days = Math.floor(up / 86400);
const hours = Math.floor((up % 86400) / 3600);
const minutes = Math.floor((up % 3600) / 60);
let str = "";
if (days > 0)
str += `${days} day${days === 1 ? "" : "s"}`;
if (hours > 0)
str += `${str ? ", " : ""}${hours} hour${hours === 1 ? "" : "s"}`;
if (minutes > 0 || !str)
str += `${str ? ", " : ""}${minutes} minute${minutes === 1 ? "" : "s"}`;
root.uptime = str;
}
}
}
+200 -193
View File
@@ -6,232 +6,239 @@ import QtQuick
import qs.Config
Singleton {
id: root
id: root
property real cpuPerc
property real cpuTemp
readonly property string gpuType: Config.services.gpuType.toUpperCase() || autoGpuType
property string autoGpuType: "NONE"
property real gpuPerc
property real gpuTemp
property real gpuMemUsed
property string autoGpuType: "NONE"
property real cpuPerc
property real cpuTemp
property real gpuMemTotal: 0
property real memUsed
property real memTotal
readonly property real memPerc: memTotal > 0 ? memUsed / memTotal : 0
property real storageUsed
property real storageTotal
property real storagePerc: storageTotal > 0 ? storageUsed / storageTotal : 0
property real gpuMemUsed
property real gpuPerc
property real gpuTemp
readonly property string gpuType: Config.services.gpuType.toUpperCase() || autoGpuType
property real lastCpuIdle
property real lastCpuTotal
readonly property real memPerc: memTotal > 0 ? memUsed / memTotal : 0
property real memTotal
property real memUsed
property int refCount
property real storagePerc: storageTotal > 0 ? storageUsed / storageTotal : 0
property real storageTotal
property real storageUsed
property real lastCpuIdle
property real lastCpuTotal
function formatKib(kib: real): var {
const mib = 1024;
const gib = 1024 ** 2;
const tib = 1024 ** 3;
property int refCount
if (kib >= tib)
return {
value: kib / tib,
unit: "TiB"
};
if (kib >= gib)
return {
value: kib / gib,
unit: "GiB"
};
if (kib >= mib)
return {
value: kib / mib,
unit: "MiB"
};
return {
value: kib,
unit: "KiB"
};
}
function formatKib(kib: real): var {
const mib = 1024;
const gib = 1024 ** 2;
const tib = 1024 ** 3;
Timer {
interval: 3000
repeat: true
running: root.refCount > 0
triggeredOnStart: true
if (kib >= tib)
return {
value: kib / tib,
unit: "TiB"
};
if (kib >= gib)
return {
value: kib / gib,
unit: "GiB"
};
if (kib >= mib)
return {
value: kib / mib,
unit: "MiB"
};
return {
value: kib,
unit: "KiB"
};
}
onTriggered: {
stat.reload();
meminfo.reload();
storage.running = true;
gpuUsage.running = true;
sensors.running = true;
}
}
Timer {
running: root.refCount > 0
interval: 3000
repeat: true
triggeredOnStart: true
onTriggered: {
stat.reload();
meminfo.reload();
storage.running = true;
gpuUsage.running = true;
sensors.running = true;
}
}
FileView {
id: stat
FileView {
id: stat
path: "/proc/stat"
path: "/proc/stat"
onLoaded: {
const data = text().match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/);
if (data) {
const stats = data.slice(1).map(n => parseInt(n, 10));
const total = stats.reduce((a, b) => a + b, 0);
const idle = stats[3] + (stats[4] ?? 0);
onLoaded: {
const data = text().match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/);
if (data) {
const stats = data.slice(1).map(n => parseInt(n, 10));
const total = stats.reduce((a, b) => a + b, 0);
const idle = stats[3] + (stats[4] ?? 0);
const totalDiff = total - root.lastCpuTotal;
const idleDiff = idle - root.lastCpuIdle;
root.cpuPerc = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0;
const totalDiff = total - root.lastCpuTotal;
const idleDiff = idle - root.lastCpuIdle;
root.cpuPerc = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0;
root.lastCpuTotal = total;
root.lastCpuIdle = idle;
}
}
}
root.lastCpuTotal = total;
root.lastCpuIdle = idle;
}
}
}
FileView {
id: meminfo
FileView {
id: meminfo
path: "/proc/meminfo"
onLoaded: {
const data = text();
root.memTotal = parseInt(data.match(/MemTotal: *(\d+)/)[1], 10) || 1;
root.memUsed = (root.memTotal - parseInt(data.match(/MemAvailable: *(\d+)/)[1], 10)) || 0;
}
}
path: "/proc/meminfo"
Process {
id: storage
onLoaded: {
const data = text();
root.memTotal = parseInt(data.match(/MemTotal: *(\d+)/)[1], 10) || 1;
root.memUsed = (root.memTotal - parseInt(data.match(/MemAvailable: *(\d+)/)[1], 10)) || 0;
}
}
command: ["sh", "-c", "df | grep '^/dev/' | awk '{print $1, $3, $4}'"]
stdout: StdioCollector {
onStreamFinished: {
const deviceMap = new Map();
Process {
id: storage
for (const line of text.trim().split("\n")) {
if (line.trim() === "")
continue;
command: ["sh", "-c", "df | grep '^/dev/' | awk '{print $1, $3, $4}'"]
const parts = line.trim().split(/\s+/);
if (parts.length >= 3) {
const device = parts[0];
const used = parseInt(parts[1], 10) || 0;
const avail = parseInt(parts[2], 10) || 0;
stdout: StdioCollector {
onStreamFinished: {
const deviceMap = new Map();
// Only keep the entry with the largest total space for each device
if (!deviceMap.has(device) || (used + avail) > (deviceMap.get(device).used + deviceMap.get(device).avail)) {
deviceMap.set(device, {
used: used,
avail: avail
});
}
}
}
for (const line of text.trim().split("\n")) {
if (line.trim() === "")
continue;
let totalUsed = 0;
let totalAvail = 0;
const parts = line.trim().split(/\s+/);
if (parts.length >= 3) {
const device = parts[0];
const used = parseInt(parts[1], 10) || 0;
const avail = parseInt(parts[2], 10) || 0;
for (const [device, stats] of deviceMap) {
totalUsed += stats.used;
totalAvail += stats.avail;
}
// Only keep the entry with the largest total space for each device
if (!deviceMap.has(device) || (used + avail) > (deviceMap.get(device).used + deviceMap.get(device).avail)) {
deviceMap.set(device, {
used: used,
avail: avail
});
}
}
}
root.storageUsed = totalUsed;
root.storageTotal = totalUsed + totalAvail;
}
}
}
let totalUsed = 0;
let totalAvail = 0;
Process {
id: gpuTypeCheck
for (const [device, stats] of deviceMap) {
totalUsed += stats.used;
totalAvail += stats.avail;
}
running: !Config.services.gpuType
command: ["sh", "-c", "if command -v nvidia-smi &>/dev/null && nvidia-smi -L &>/dev/null; then echo NVIDIA; elif ls /sys/class/drm/card*/device/gpu_busy_percent 2>/dev/null | grep -q .; then echo GENERIC; else echo NONE; fi"]
stdout: StdioCollector {
onStreamFinished: root.autoGpuType = text.trim()
}
}
root.storageUsed = totalUsed;
root.storageTotal = totalUsed + totalAvail;
}
}
}
Process {
id: oneshotMem
command: ["nvidia-smi", "--query-gpu=memory.total", "--format=csv,noheader,nounits"]
running: root.gpuType === "NVIDIA" && root.gpuMemTotal === 0
stdout: StdioCollector {
onStreamFinished: {
root.gpuMemTotal = Number(this.text.trim())
oneshotMem.running = false
}
}
}
Process {
id: gpuTypeCheck
Process {
id: gpuUsage
command: ["sh", "-c", "if command -v nvidia-smi &>/dev/null && nvidia-smi -L &>/dev/null; then echo NVIDIA; elif ls /sys/class/drm/card*/device/gpu_busy_percent 2>/dev/null | grep -q .; then echo GENERIC; else echo NONE; fi"]
running: !Config.services.gpuType
command: root.gpuType === "GENERIC" ? ["sh", "-c", "cat /sys/class/drm/card*/device/gpu_busy_percent"] : root.gpuType === "NVIDIA" ? ["nvidia-smi", "--query-gpu=utilization.gpu,temperature.gpu,memory.used", "--format=csv,noheader,nounits"] : ["echo"]
stdout: StdioCollector {
onStreamFinished: {
if (root.gpuType === "GENERIC") {
const percs = text.trim().split("\n");
const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0);
root.gpuPerc = sum / percs.length / 100;
} else if (root.gpuType === "NVIDIA") {
const [usage, temp, mem] = text.trim().split(",");
root.gpuPerc = parseInt(usage, 10) / 100;
root.gpuTemp = parseInt(temp, 10);
stdout: StdioCollector {
onStreamFinished: root.autoGpuType = text.trim()
}
}
Process {
id: oneshotMem
command: ["nvidia-smi", "--query-gpu=memory.total", "--format=csv,noheader,nounits"]
running: root.gpuType === "NVIDIA" && root.gpuMemTotal === 0
stdout: StdioCollector {
onStreamFinished: {
root.gpuMemTotal = Number(this.text.trim());
oneshotMem.running = false;
}
}
}
Process {
id: gpuUsage
command: root.gpuType === "GENERIC" ? ["sh", "-c", "cat /sys/class/drm/card*/device/gpu_busy_percent"] : root.gpuType === "NVIDIA" ? ["nvidia-smi", "--query-gpu=utilization.gpu,temperature.gpu,memory.used", "--format=csv,noheader,nounits"] : ["echo"]
stdout: StdioCollector {
onStreamFinished: {
if (root.gpuType === "GENERIC") {
const percs = text.trim().split("\n");
const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0);
root.gpuPerc = sum / percs.length / 100;
} else if (root.gpuType === "NVIDIA") {
const [usage, temp, mem] = text.trim().split(",");
root.gpuPerc = parseInt(usage, 10) / 100;
root.gpuTemp = parseInt(temp, 10);
root.gpuMemUsed = parseInt(mem, 10) / root.gpuMemTotal;
} else {
root.gpuPerc = 0;
root.gpuTemp = 0;
}
}
}
}
} else {
root.gpuPerc = 0;
root.gpuTemp = 0;
}
}
}
}
Process {
id: sensors
Process {
id: sensors
command: ["sensors"]
environment: ({
LANG: "C.UTF-8",
LC_ALL: "C.UTF-8"
})
stdout: StdioCollector {
onStreamFinished: {
let cpuTemp = text.match(/(?:Package id [0-9]+|Tdie):\s+((\+|-)[0-9.]+)(°| )C/);
if (!cpuTemp)
// If AMD Tdie pattern failed, try fallback on Tctl
cpuTemp = text.match(/Tctl:\s+((\+|-)[0-9.]+)(°| )C/);
command: ["sensors"]
environment: ({
LANG: "C.UTF-8",
LC_ALL: "C.UTF-8"
})
if (cpuTemp)
root.cpuTemp = parseFloat(cpuTemp[1]);
stdout: StdioCollector {
onStreamFinished: {
let cpuTemp = text.match(/(?:Package id [0-9]+|Tdie):\s+((\+|-)[0-9.]+)(°| )C/);
if (!cpuTemp)
// If AMD Tdie pattern failed, try fallback on Tctl
cpuTemp = text.match(/Tctl:\s+((\+|-)[0-9.]+)(°| )C/);
if (root.gpuType !== "GENERIC")
return;
if (cpuTemp)
root.cpuTemp = parseFloat(cpuTemp[1]);
let eligible = false;
let sum = 0;
let count = 0;
if (root.gpuType !== "GENERIC")
return;
for (const line of text.trim().split("\n")) {
if (line === "Adapter: PCI adapter")
eligible = true;
else if (line === "")
eligible = false;
else if (eligible) {
let match = line.match(/^(temp[0-9]+|GPU core|edge)+:\s+\+([0-9]+\.[0-9]+)(°| )C/);
if (!match)
// Fall back to junction/mem if GPU doesn't have edge temp (for AMD GPUs)
match = line.match(/^(junction|mem)+:\s+\+([0-9]+\.[0-9]+)(°| )C/);
let eligible = false;
let sum = 0;
let count = 0;
if (match) {
sum += parseFloat(match[2]);
count++;
}
}
}
for (const line of text.trim().split("\n")) {
if (line === "Adapter: PCI adapter")
eligible = true;
else if (line === "")
eligible = false;
else if (eligible) {
let match = line.match(/^(temp[0-9]+|GPU core|edge)+:\s+\+([0-9]+\.[0-9]+)(°| )C/);
if (!match)
// Fall back to junction/mem if GPU doesn't have edge temp (for AMD GPUs)
match = line.match(/^(junction|mem)+:\s+\+([0-9]+\.[0-9]+)(°| )C/);
root.gpuTemp = count > 0 ? sum / count : 0;
}
}
}
if (match) {
sum += parseFloat(match[2]);
count++;
}
}
}
root.gpuTemp = count > 0 ? sum / count : 0;
}
}
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
import QtQuick
Text {
renderType: Text.NativeRendering
textFormat: Text.PlainText
renderType: Text.NativeRendering
textFormat: Text.PlainText
}
+189 -187
View File
@@ -6,6 +6,30 @@ import Quickshell
Singleton {
id: root
property list<DesktopEntry> entryList: []
property var preppedIcons: []
property var preppedIds: []
property var preppedNames: []
// Dynamic fixups
property var regexSubstitutions: [
{
"regex": /^steam_app_(\d+)$/,
"replace": "steam_icon_$1"
},
{
"regex": /Minecraft.*/,
"replace": "minecraft-launcher"
},
{
"regex": /.*polkit.*/,
"replace": "system-lock-screen"
},
{
"regex": /gcr.prompter/,
"replace": "system-lock-screen"
}
]
property real scoreThreshold: 0.2
// Manual overrides for tricky apps
@@ -17,178 +41,23 @@ Singleton {
"wps": "wps-office2019-kprometheus",
"wpsoffice": "wps-office2019-kprometheus",
"footclient": "foot"
})
})
// Dynamic fixups
property var regexSubstitutions: [
{
"regex": /^steam_app_(\d+)$/,
"replace": "steam_icon_$1"
},
{
"regex": /Minecraft.*/,
"replace": "minecraft-launcher"
},
{
"regex": /.*polkit.*/,
"replace": "system-lock-screen"
},
{
"regex": /gcr.prompter/,
"replace": "system-lock-screen"
}
]
property list<DesktopEntry> entryList: []
property var preppedNames: []
property var preppedIcons: []
property var preppedIds: []
Component.onCompleted: refreshEntries()
Connections {
target: DesktopEntries.applications
function onValuesChanged() {
refreshEntries();
}
}
function refreshEntries() {
if (typeof DesktopEntries === 'undefined')
return;
const values = Array.from(DesktopEntries.applications.values);
if (values) {
entryList = values.sort((a, b) => a.name.localeCompare(b.name));
updatePreppedData();
}
}
function updatePreppedData() {
if (typeof FuzzySort === 'undefined')
return;
const list = Array.from(entryList);
preppedNames = list.map(a => ({
name: FuzzySort.prepare(`${a.name} `), entry: a}));
preppedIcons = list.map(a => ({
name: FuzzySort.prepare(`${a.icon} `),
entry: a
}));
preppedIds = list.map(a => ({
name: FuzzySort.prepare(`${a.id} `),
entry: a
}));
}
function iconForAppId(appId, fallbackName) {
const fallback = fallbackName || "application-x-executable";
if (!appId)
return iconFromName(fallback, fallback);
const entry = findAppEntry(appId);
if (entry) {
return iconFromName(entry.icon, fallback);
}
return iconFromName(appId, fallback);
}
// Robust lookup strategy
function findAppEntry(str) {
if (!str || str.length === 0)
function checkCleanMatch(str) {
if (!str || str.length <= 3)
return null;
let result = null;
if (result = checkHeuristic(str))
return result;
if (result = checkSubstitutions(str))
return result;
if (result = checkRegex(str))
return result;
if (result = checkSimpleTransforms(str))
return result;
if (result = checkFuzzySearch(str))
return result;
if (result = checkCleanMatch(str))
return result;
return null;
}
function iconFromName(iconName, fallbackName) {
const fallback = fallbackName || "application-x-executable";
try {
if (iconName && typeof Quickshell !== 'undefined' && Quickshell.iconPath) {
const p = Quickshell.iconPath(iconName, fallback);
if (p && p !== "")
return p;
}
} catch (e) {}
try {
return Quickshell.iconPath ? (Quickshell.iconPath(fallback, true) || "") : "";
} catch (e2) {
return "";
}
}
function distroLogoPath() {
try {
return (typeof OSInfo !== 'undefined' && OSInfo.distroIconPath) ? OSInfo.distroIconPath : "";
} catch (e) {
return "";
}
}
// --- Lookup Helpers ---
function checkHeuristic(str) {
if (typeof DesktopEntries !== 'undefined' && DesktopEntries.heuristicLookup) {
const entry = DesktopEntries.heuristicLookup(str);
if (entry)
return entry;
}
return null;
}
function checkSubstitutions(str) {
let effectiveStr = substitutions[str];
if (!effectiveStr)
effectiveStr = substitutions[str.toLowerCase()];
if (effectiveStr && effectiveStr !== str) {
return findAppEntry(effectiveStr);
}
return null;
}
function checkRegex(str) {
for (let i = 0; i < regexSubstitutions.length; i++) {
const sub = regexSubstitutions[i];
const replaced = str.replace(sub.regex, sub.replace);
if (replaced !== str) {
return findAppEntry(replaced);
}
}
return null;
}
function checkSimpleTransforms(str) {
if (typeof DesktopEntries === 'undefined' || !DesktopEntries.byId)
return null;
const lower = str.toLowerCase();
// Aggressive fallback: strip all separators
const cleanStr = str.toLowerCase().replace(/[\.\-_]/g, '');
const list = Array.from(entryList);
const variants = [str, lower, getFromReverseDomain(str), getFromReverseDomain(str)?.toLowerCase(), normalizeWithHyphens(str), str.replace(/_/g, '-').toLowerCase(), str.replace(/-/g, '_').toLowerCase()];
for (let i = 0; i < variants.length; i++) {
const variant = variants[i];
if (variant) {
const entry = DesktopEntries.byId(variant);
if (entry)
return entry;
for (let i = 0; i < list.length; i++) {
const entry = list[i];
const cleanId = (entry.id || "").toLowerCase().replace(/[\.\-_]/g, '');
if (cleanId.includes(cleanStr) || cleanStr.includes(cleanId)) {
return entry;
}
}
return null;
@@ -227,33 +96,108 @@ Singleton {
return null;
}
function checkCleanMatch(str) {
if (!str || str.length <= 3)
return null;
// --- Lookup Helpers ---
function checkHeuristic(str) {
if (typeof DesktopEntries !== 'undefined' && DesktopEntries.heuristicLookup) {
const entry = DesktopEntries.heuristicLookup(str);
if (entry)
return entry;
}
return null;
}
function checkRegex(str) {
for (let i = 0; i < regexSubstitutions.length; i++) {
const sub = regexSubstitutions[i];
const replaced = str.replace(sub.regex, sub.replace);
if (replaced !== str) {
return findAppEntry(replaced);
}
}
return null;
}
function checkSimpleTransforms(str) {
if (typeof DesktopEntries === 'undefined' || !DesktopEntries.byId)
return null;
// Aggressive fallback: strip all separators
const cleanStr = str.toLowerCase().replace(/[\.\-_]/g, '');
const list = Array.from(entryList);
const lower = str.toLowerCase();
for (let i = 0; i < list.length; i++) {
const entry = list[i];
const cleanId = (entry.id || "").toLowerCase().replace(/[\.\-_]/g, '');
if (cleanId.includes(cleanStr) || cleanStr.includes(cleanId)) {
return entry;
const variants = [str, lower, getFromReverseDomain(str), getFromReverseDomain(str)?.toLowerCase(), normalizeWithHyphens(str), str.replace(/_/g, '-').toLowerCase(), str.replace(/-/g, '_').toLowerCase()];
for (let i = 0; i < variants.length; i++) {
const variant = variants[i];
if (variant) {
const entry = DesktopEntries.byId(variant);
if (entry)
return entry;
}
}
return null;
}
function checkSubstitutions(str) {
let effectiveStr = substitutions[str];
if (!effectiveStr)
effectiveStr = substitutions[str.toLowerCase()];
if (effectiveStr && effectiveStr !== str) {
return findAppEntry(effectiveStr);
}
return null;
}
function distroLogoPath() {
try {
return (typeof OSInfo !== 'undefined' && OSInfo.distroIconPath) ? OSInfo.distroIconPath : "";
} catch (e) {
return "";
}
}
// Robust lookup strategy
function findAppEntry(str) {
if (!str || str.length === 0)
return null;
let result = null;
if (result = checkHeuristic(str))
return result;
if (result = checkSubstitutions(str))
return result;
if (result = checkRegex(str))
return result;
if (result = checkSimpleTransforms(str))
return result;
if (result = checkFuzzySearch(str))
return result;
if (result = checkCleanMatch(str))
return result;
return null;
}
function fuzzyQuery(search, preppedData) {
if (!search || !preppedData || preppedData.length === 0)
return [];
return FuzzySort.go(search, preppedData, {
all: true,
key: "name"
}).map(r => r.obj.entry);
all: true,
key: "name"
}).map(r => r.obj.entry);
}
function getFromReverseDomain(str) {
if (!str)
return "";
return str.split('.').slice(-1)[0];
}
// Deprecated shim
function guessIcon(str) {
const entry = findAppEntry(str);
return entry ? entry.icon : "image-missing";
}
function iconExists(iconName) {
@@ -266,10 +210,34 @@ Singleton {
return path && path.length > 0 && !path.includes("image-missing");
}
function getFromReverseDomain(str) {
if (!str)
function iconForAppId(appId, fallbackName) {
const fallback = fallbackName || "application-x-executable";
if (!appId)
return iconFromName(fallback, fallback);
const entry = findAppEntry(appId);
if (entry) {
return iconFromName(entry.icon, fallback);
}
return iconFromName(appId, fallback);
}
function iconFromName(iconName, fallbackName) {
const fallback = fallbackName || "application-x-executable";
try {
if (iconName && typeof Quickshell !== 'undefined' && Quickshell.iconPath) {
const p = Quickshell.iconPath(iconName, fallback);
if (p && p !== "")
return p;
}
} catch (e) {}
try {
return Quickshell.iconPath ? (Quickshell.iconPath(fallback, true) || "") : "";
} catch (e2) {
return "";
return str.split('.').slice(-1)[0];
}
}
function normalizeWithHyphens(str) {
@@ -278,9 +246,43 @@ Singleton {
return str.toLowerCase().replace(/\s+/g, "-");
}
// Deprecated shim
function guessIcon(str) {
const entry = findAppEntry(str);
return entry ? entry.icon : "image-missing";
function refreshEntries() {
if (typeof DesktopEntries === 'undefined')
return;
const values = Array.from(DesktopEntries.applications.values);
if (values) {
entryList = values.sort((a, b) => a.name.localeCompare(b.name));
updatePreppedData();
}
}
function updatePreppedData() {
if (typeof FuzzySort === 'undefined')
return;
const list = Array.from(entryList);
preppedNames = list.map(a => ({
name: FuzzySort.prepare(`${a.name} `),
entry: a
}));
preppedIcons = list.map(a => ({
name: FuzzySort.prepare(`${a.icon} `),
entry: a
}));
preppedIds = list.map(a => ({
name: FuzzySort.prepare(`${a.id} `),
entry: a
}));
}
Component.onCompleted: refreshEntries()
Connections {
function onValuesChanged() {
refreshEntries();
}
target: DesktopEntries.applications
}
}
+17 -17
View File
@@ -3,24 +3,24 @@ pragma Singleton
import Quickshell
Singleton {
property alias enabled: clock.enabled
readonly property date date: clock.date
readonly property int hours: clock.hours
readonly property int minutes: clock.minutes
readonly property int seconds: clock.seconds
readonly property string amPmStr: timeComponents[2] ?? ""
readonly property date date: clock.date
property alias enabled: clock.enabled
readonly property string hourStr: timeComponents[0] ?? ""
readonly property int hours: clock.hours
readonly property string minuteStr: timeComponents[1] ?? ""
readonly property int minutes: clock.minutes
readonly property int seconds: clock.seconds
readonly property list<string> timeComponents: timeStr.split(":")
readonly property string timeStr: format("hh:mm")
readonly property string timeStr: format("hh:mm")
readonly property list<string> timeComponents: timeStr.split(":")
readonly property string hourStr: timeComponents[0] ?? ""
readonly property string minuteStr: timeComponents[1] ?? ""
readonly property string amPmStr: timeComponents[2] ?? ""
function format(fmt: string): string {
return Qt.formatDateTime(clock.date, fmt);
}
function format(fmt: string): string {
return Qt.formatDateTime(clock.date, fmt);
}
SystemClock {
id: clock
SystemClock {
id: clock
precision: SystemClock.Seconds
}
precision: SystemClock.Seconds
}
}
+1 -2
View File
@@ -3,11 +3,10 @@ pragma Singleton
import Quickshell
import Quickshell.Services.UPower
Singleton {
id: root
readonly property list<UPowerDevice> devices: UPower.devices.values
readonly property bool onBattery: UPower.onBattery
readonly property UPowerDevice displayDevice: UPower.displayDevice
readonly property bool onBattery: UPower.onBattery
}
+8 -8
View File
@@ -3,14 +3,14 @@ pragma Singleton
import Quickshell
Singleton {
property var screens: new Map()
property var bars: new Map()
property var bars: new Map()
property var screens: new Map()
function load(screen: ShellScreen, visibilities: var): void {
screens.set(Hypr.monitorFor(screen), visibilities);
}
function getForActive(): PersistentProperties {
return screens.get(Hypr.focusedMonitor);
}
function getForActive(): PersistentProperties {
return screens.get(Hypr.focusedMonitor);
}
function load(screen: ShellScreen, visibilities: var): void {
screens.set(Hypr.monitorFor(screen), visibilities);
}
}
+16 -13
View File
@@ -5,22 +5,25 @@ import Quickshell.Io
import qs.Paths
Singleton {
id: root
id: root
property alias currentWallpaperPath: adapter.currentWallpaperPath
property alias currentWallpaperPath: adapter.currentWallpaperPath
property alias lockscreenBg: adapter.lockscreenBg
FileView {
id: fileView
path: `${Paths.state}/wallpaper_path.json`
FileView {
id: fileView
watchChanges: true
onFileChanged: reload()
onAdapterUpdated: writeAdapter()
JsonAdapter {
id: adapter
property string currentWallpaperPath: ""
path: `${Paths.state}/wallpaper_path.json`
watchChanges: true
onAdapterUpdated: writeAdapter()
onFileChanged: reload()
JsonAdapter {
id: adapter
property string currentWallpaperPath: ""
property string lockscreenBg: `${Paths.state}/lockscreen_bg.png`
}
}
}
}
}
+35 -35
View File
@@ -9,54 +9,54 @@ import qs.Helpers
import qs.Paths
Searcher {
id: root
id: root
property bool showPreview: false
readonly property string current: showPreview ? previewPath : actualCurrent
property string previewPath
property string actualCurrent: WallpaperPath.currentWallpaperPath
property string actualCurrent: WallpaperPath.currentWallpaperPath
readonly property string current: showPreview ? previewPath : actualCurrent
property string previewPath
property bool showPreview: false
function setWallpaper(path: string): void {
actualCurrent = path;
WallpaperPath.currentWallpaperPath = path;
if ( Config.general.color.wallust )
function preview(path: string): void {
previewPath = path;
if (Config.general.color.schemeGeneration)
Quickshell.execDetached(["sh", "-c", `zshell-cli scheme generate --image-path ${previewPath} --thumbnail-path ${Paths.cache}/imagecache/thumbnail.jpg --output ${Paths.state}/scheme.json --scheme ${Config.colors.schemeType} --mode ${Config.general.color.mode}`]);
showPreview = true;
}
function setWallpaper(path: string): void {
actualCurrent = path;
WallpaperPath.currentWallpaperPath = path;
if (Config.general.color.wallust)
Wallust.generateColors(WallpaperPath.currentWallpaperPath);
Quickshell.execDetached(["sh", "-c", `zshell-cli wallpaper lockscreen --input-image=${root.actualCurrent} --output-path=${Paths.state}/lockscreen_bg.png --blur-amount=${Config.lock.blurAmount}`]);
}
}
function preview(path: string): void {
previewPath = path;
if ( Config.general.color.schemeGeneration )
Quickshell.execDetached(["sh", "-c", `zshell-cli scheme generate --image-path ${previewPath} --thumbnail-path ${Paths.cache}/imagecache/thumbnail.jpg --output ${Paths.state}/scheme.json --scheme ${Config.colors.schemeType} --mode ${Config.general.color.mode}`]);
showPreview = true;
}
function stopPreview(): void {
showPreview = false;
if ( Config.general.color.schemeGeneration )
function stopPreview(): void {
showPreview = false;
if (Config.general.color.schemeGeneration)
Quickshell.execDetached(["sh", "-c", `zshell-cli scheme generate --image-path ${root.actualCurrent} --thumbnail-path ${Paths.cache}/imagecache/thumbnail.jpg --output ${Paths.state}/scheme.json --scheme ${Config.colors.schemeType} --mode ${Config.general.color.mode}`]);
}
}
list: wallpapers.entries
key: "relativePath"
useFuzzy: true
extraOpts: useFuzzy ? ({}) : ({
forward: false
})
extraOpts: useFuzzy ? ({}) : ({
forward: false
})
key: "relativePath"
list: wallpapers.entries
useFuzzy: true
IpcHandler {
target: "wallpaper"
function set(path: string): void {
root.setWallpaper(path);
}
target: "wallpaper"
}
FileSystemModel {
id: wallpapers
FileSystemModel {
id: wallpapers
recursive: true
path: Config.general.wallpaperPath
filter: FileSystemModel.Images
}
filter: FileSystemModel.Images
path: Config.general.wallpaperPath
recursive: true
}
}
+12 -11
View File
@@ -6,20 +6,21 @@ import QtQuick
import qs.Config
Singleton {
id: root
id: root
property var args
property var args
readonly property string mode: Config.general.color.mode
readonly property string threshold: mode === "dark" ? "--threshold=9" : "--dynamic-threshold"
function generateColors(wallpaperPath) {
root.args = wallpaperPath;
wallustProc.running = true;
}
function generateColors(wallpaperPath) {
root.args = wallpaperPath;
wallustProc.running = true;
}
Process {
id: wallustProc
command: ["wallust", "run", root.args, `--palette=${root.mode}`, "--ignore-sequence=cursor", `${root.threshold}` ]
running: false
}
Process {
id: wallustProc
command: ["wallust", "run", root.args, `--palette=${root.mode}`, "--ignore-sequence=cursor", `${root.threshold}`]
running: false
}
}
+173 -173
View File
@@ -6,200 +6,200 @@ import ZShell
import qs.Config
Singleton {
id: root
id: root
property string city
property string loc
property var cc
property list<var> forecast
property list<var> hourlyForecast
readonly property var cachedCities: new Map()
property var cc
property string city
readonly property string description: cc?.weatherDesc ?? qsTr("No weather")
readonly property string feelsLike: `${cc?.feelsLikeC ?? 0}°C`
property list<var> forecast
property list<var> hourlyForecast
readonly property int humidity: cc?.humidity ?? 0
readonly property string icon: cc ? Icons.getWeatherIcon(cc.weatherCode) : "cloud_alert"
property string loc
readonly property string sunrise: cc ? Qt.formatDateTime(new Date(cc.sunrise), "h:mm") : "--:--"
readonly property string sunset: cc ? Qt.formatDateTime(new Date(cc.sunset), "h:mm") : "--:--"
readonly property string temp: `${cc?.tempC ?? 0}°C`
readonly property real windSpeed: cc?.windSpeed ?? 0
readonly property string icon: cc ? Icons.getWeatherIcon(cc.weatherCode) : "cloud_alert"
readonly property string description: cc?.weatherDesc ?? qsTr("No weather")
readonly property string temp: `${cc?.tempC ?? 0}°C`
readonly property string feelsLike: `${cc?.feelsLikeC ?? 0}°C`
readonly property int humidity: cc?.humidity ?? 0
readonly property real windSpeed: cc?.windSpeed ?? 0
readonly property string sunrise: cc ? Qt.formatDateTime(new Date(cc.sunrise), "h:mm") : "--:--"
readonly property string sunset: cc ? Qt.formatDateTime(new Date(cc.sunset), "h:mm") : "--:--"
function fetchCityFromCoords(coords: string): void {
if (cachedCities.has(coords)) {
city = cachedCities.get(coords);
return;
}
readonly property var cachedCities: new Map()
const [lat, lon] = coords.split(",");
const url = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=geocodejson`;
Requests.get(url, text => {
const geo = JSON.parse(text).features?.[0]?.properties.geocoding;
if (geo) {
const geoCity = geo.type === "city" ? geo.name : geo.city;
city = geoCity;
cachedCities.set(coords, geoCity);
} else {
city = "Unknown City";
}
});
}
function reload(): void {
const configLocation = Config.services.weatherLocation;
function fetchCoordsFromCity(cityName: string): void {
const url = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(cityName)}&count=1&language=en&format=json`;
if (configLocation) {
if (configLocation.indexOf(",") !== -1 && !isNaN(parseFloat(configLocation.split(",")[0]))) {
loc = configLocation;
fetchCityFromCoords(configLocation);
} else {
fetchCoordsFromCity(configLocation);
}
} else if (!loc || timer.elapsed() > 900) {
Requests.get("https://ipinfo.io/json", text => {
const response = JSON.parse(text);
if (response.loc) {
loc = response.loc;
city = response.city ?? "";
timer.restart();
}
});
}
}
Requests.get(url, text => {
const json = JSON.parse(text);
if (json.results && json.results.length > 0) {
const result = json.results[0];
loc = result.latitude + "," + result.longitude;
city = result.name;
} else {
loc = "";
reload();
}
});
}
function fetchCityFromCoords(coords: string): void {
if (cachedCities.has(coords)) {
city = cachedCities.get(coords);
return;
}
function fetchWeatherData(): void {
const url = getWeatherUrl();
if (url === "")
return;
const [lat, lon] = coords.split(",");
const url = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=geocodejson`;
Requests.get(url, text => {
const geo = JSON.parse(text).features?.[0]?.properties.geocoding;
if (geo) {
const geoCity = geo.type === "city" ? geo.name : geo.city;
city = geoCity;
cachedCities.set(coords, geoCity);
} else {
city = "Unknown City";
}
});
}
Requests.get(url, text => {
const json = JSON.parse(text);
if (!json.current || !json.daily)
return;
function fetchCoordsFromCity(cityName: string): void {
const url = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(cityName)}&count=1&language=en&format=json`;
cc = {
weatherCode: json.current.weather_code,
weatherDesc: getWeatherCondition(json.current.weather_code),
tempC: Math.round(json.current.temperature_2m),
tempF: Math.round(toFahrenheit(json.current.temperature_2m)),
feelsLikeC: Math.round(json.current.apparent_temperature),
feelsLikeF: Math.round(toFahrenheit(json.current.apparent_temperature)),
humidity: json.current.relative_humidity_2m,
windSpeed: json.current.wind_speed_10m,
isDay: json.current.is_day,
sunrise: json.daily.sunrise[0],
sunset: json.daily.sunset[0]
};
Requests.get(url, text => {
const json = JSON.parse(text);
if (json.results && json.results.length > 0) {
const result = json.results[0];
loc = result.latitude + "," + result.longitude;
city = result.name;
} else {
loc = "";
reload();
}
});
}
const forecastList = [];
for (let i = 0; i < json.daily.time.length; i++)
forecastList.push({
date: json.daily.time[i],
maxTempC: Math.round(json.daily.temperature_2m_max[i]),
maxTempF: Math.round(toFahrenheit(json.daily.temperature_2m_max[i])),
minTempC: Math.round(json.daily.temperature_2m_min[i]),
minTempF: Math.round(toFahrenheit(json.daily.temperature_2m_min[i])),
weatherCode: json.daily.weather_code[i],
icon: Icons.getWeatherIcon(json.daily.weather_code[i])
});
forecast = forecastList;
function fetchWeatherData(): void {
const url = getWeatherUrl();
if (url === "")
return;
const hourlyList = [];
const now = new Date();
for (let i = 0; i < json.hourly.time.length; i++) {
const time = new Date(json.hourly.time[i]);
if (time < now)
continue;
Requests.get(url, text => {
const json = JSON.parse(text);
if (!json.current || !json.daily)
return;
hourlyList.push({
timestamp: json.hourly.time[i],
hour: time.getHours(),
tempC: Math.round(json.hourly.temperature_2m[i]),
tempF: Math.round(toFahrenheit(json.hourly.temperature_2m[i])),
weatherCode: json.hourly.weather_code[i],
icon: Icons.getWeatherIcon(json.hourly.weather_code[i])
});
}
hourlyForecast = hourlyList;
});
}
cc = {
weatherCode: json.current.weather_code,
weatherDesc: getWeatherCondition(json.current.weather_code),
tempC: Math.round(json.current.temperature_2m),
tempF: Math.round(toFahrenheit(json.current.temperature_2m)),
feelsLikeC: Math.round(json.current.apparent_temperature),
feelsLikeF: Math.round(toFahrenheit(json.current.apparent_temperature)),
humidity: json.current.relative_humidity_2m,
windSpeed: json.current.wind_speed_10m,
isDay: json.current.is_day,
sunrise: json.daily.sunrise[0],
sunset: json.daily.sunset[0]
};
function getWeatherCondition(code: string): string {
const conditions = {
"0": "Clear",
"1": "Clear",
"2": "Partly cloudy",
"3": "Overcast",
"45": "Fog",
"48": "Fog",
"51": "Drizzle",
"53": "Drizzle",
"55": "Drizzle",
"56": "Freezing drizzle",
"57": "Freezing drizzle",
"61": "Light rain",
"63": "Rain",
"65": "Heavy rain",
"66": "Light rain",
"67": "Heavy rain",
"71": "Light snow",
"73": "Snow",
"75": "Heavy snow",
"77": "Snow",
"80": "Light rain",
"81": "Rain",
"82": "Heavy rain",
"85": "Light snow showers",
"86": "Heavy snow showers",
"95": "Thunderstorm",
"96": "Thunderstorm with hail",
"99": "Thunderstorm with hail"
};
return conditions[code] || "Unknown";
}
const forecastList = [];
for (let i = 0; i < json.daily.time.length; i++)
forecastList.push({
date: json.daily.time[i],
maxTempC: Math.round(json.daily.temperature_2m_max[i]),
maxTempF: Math.round(toFahrenheit(json.daily.temperature_2m_max[i])),
minTempC: Math.round(json.daily.temperature_2m_min[i]),
minTempF: Math.round(toFahrenheit(json.daily.temperature_2m_min[i])),
weatherCode: json.daily.weather_code[i],
icon: Icons.getWeatherIcon(json.daily.weather_code[i])
});
forecast = forecastList;
function getWeatherUrl(): string {
if (!loc || loc.indexOf(",") === -1)
return "";
const hourlyList = [];
const now = new Date();
for (let i = 0; i < json.hourly.time.length; i++) {
const time = new Date(json.hourly.time[i]);
if (time < now)
continue;
const [lat, lon] = loc.split(",");
const baseUrl = "https://api.open-meteo.com/v1/forecast";
const params = ["latitude=" + lat, "longitude=" + lon, "hourly=weather_code,temperature_2m", "daily=weather_code,temperature_2m_max,temperature_2m_min,sunrise,sunset", "current=temperature_2m,relative_humidity_2m,apparent_temperature,is_day,weather_code,wind_speed_10m", "timezone=auto", "forecast_days=7"];
hourlyList.push({
timestamp: json.hourly.time[i],
hour: time.getHours(),
tempC: Math.round(json.hourly.temperature_2m[i]),
tempF: Math.round(toFahrenheit(json.hourly.temperature_2m[i])),
weatherCode: json.hourly.weather_code[i],
icon: Icons.getWeatherIcon(json.hourly.weather_code[i])
});
}
hourlyForecast = hourlyList;
});
}
return baseUrl + "?" + params.join("&");
}
function toFahrenheit(celcius: real): real {
return celcius * 9 / 5 + 32;
}
function reload(): void {
const configLocation = Config.services.weatherLocation;
function getWeatherUrl(): string {
if (!loc || loc.indexOf(",") === -1)
return "";
if (configLocation) {
if (configLocation.indexOf(",") !== -1 && !isNaN(parseFloat(configLocation.split(",")[0]))) {
loc = configLocation;
fetchCityFromCoords(configLocation);
} else {
fetchCoordsFromCity(configLocation);
}
} else if (!loc || timer.elapsed() > 900) {
Requests.get("https://ipinfo.io/json", text => {
const response = JSON.parse(text);
if (response.loc) {
loc = response.loc;
city = response.city ?? "";
timer.restart();
}
});
}
}
const [lat, lon] = loc.split(",");
const baseUrl = "https://api.open-meteo.com/v1/forecast";
const params = ["latitude=" + lat, "longitude=" + lon, "hourly=weather_code,temperature_2m", "daily=weather_code,temperature_2m_max,temperature_2m_min,sunrise,sunset", "current=temperature_2m,relative_humidity_2m,apparent_temperature,is_day,weather_code,wind_speed_10m", "timezone=auto", "forecast_days=7"];
function toFahrenheit(celcius: real): real {
return celcius * 9 / 5 + 32;
}
return baseUrl + "?" + params.join("&");
}
onLocChanged: fetchWeatherData()
function getWeatherCondition(code: string): string {
const conditions = {
"0": "Clear",
"1": "Clear",
"2": "Partly cloudy",
"3": "Overcast",
"45": "Fog",
"48": "Fog",
"51": "Drizzle",
"53": "Drizzle",
"55": "Drizzle",
"56": "Freezing drizzle",
"57": "Freezing drizzle",
"61": "Light rain",
"63": "Rain",
"65": "Heavy rain",
"66": "Light rain",
"67": "Heavy rain",
"71": "Light snow",
"73": "Snow",
"75": "Heavy snow",
"77": "Snow",
"80": "Light rain",
"81": "Rain",
"82": "Heavy rain",
"85": "Light snow showers",
"86": "Heavy snow showers",
"95": "Thunderstorm",
"96": "Thunderstorm with hail",
"99": "Thunderstorm with hail"
};
return conditions[code] || "Unknown";
}
// Refresh current location hourly
Timer {
interval: 3600000 // 1 hour
repeat: true
running: true
onLocChanged: fetchWeatherData()
onTriggered: fetchWeatherData()
}
// Refresh current location hourly
Timer {
interval: 3600000 // 1 hour
running: true
repeat: true
onTriggered: fetchWeatherData()
}
ElapsedTimer {
id: timer
ElapsedTimer {
id: timer
}
}
}
+52 -416
View File
@@ -1,420 +1,56 @@
import Quickshell.Io
JsonObject {
property list<var> week_0: [
0,
0,
0,
0,
0,
0,
]
property list<var> week_1: [
0,
0,
0,
0,
0,
0,
]
property list<var> week_2: [
0,
0,
0,
0,
0,
0,
]
property list<var> week_3: [
0,
0,
0,
0,
0,
0,
]
property list<var> week_4: [
0,
0,
0,
0,
0,
0,
]
property list<var> week_5: [
0,
0,
0,
0,
0,
0,
]
property list<var> week_6: [
0,
0,
0,
0,
0,
0,
]
property list<var> week_7: [
0,
0,
0,
0,
0,
0,
]
property list<var> week_8: [
0,
0,
0,
0,
0,
0,
]
property list<var> week_9: [
0,
0,
0,
0,
0,
0,
]
property list<var> week_10:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_11:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_12:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_13:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_14:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_15:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_16:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_17:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_18:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_19:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_20:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_21:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_22:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_23:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_24:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_25:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_26:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_27:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_28:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_29:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_30:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_31:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_32:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_33:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_34:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_35:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_36:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_37:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_38:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_39:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_40:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_41:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_42:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_43:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_44:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_45:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_46:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_47:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_48:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_49:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_50:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_51:[
0,
0,
0,
0,
0,
0,
]
property list<var> week_0: [0, 0, 0, 0, 0, 0,]
property list<var> week_1: [0, 0, 0, 0, 0, 0,]
property list<var> week_10: [0, 0, 0, 0, 0, 0,]
property list<var> week_11: [0, 0, 0, 0, 0, 0,]
property list<var> week_12: [0, 0, 0, 0, 0, 0,]
property list<var> week_13: [0, 0, 0, 0, 0, 0,]
property list<var> week_14: [0, 0, 0, 0, 0, 0,]
property list<var> week_15: [0, 0, 0, 0, 0, 0,]
property list<var> week_16: [0, 0, 0, 0, 0, 0,]
property list<var> week_17: [0, 0, 0, 0, 0, 0,]
property list<var> week_18: [0, 0, 0, 0, 0, 0,]
property list<var> week_19: [0, 0, 0, 0, 0, 0,]
property list<var> week_2: [0, 0, 0, 0, 0, 0,]
property list<var> week_20: [0, 0, 0, 0, 0, 0,]
property list<var> week_21: [0, 0, 0, 0, 0, 0,]
property list<var> week_22: [0, 0, 0, 0, 0, 0,]
property list<var> week_23: [0, 0, 0, 0, 0, 0,]
property list<var> week_24: [0, 0, 0, 0, 0, 0,]
property list<var> week_25: [0, 0, 0, 0, 0, 0,]
property list<var> week_26: [0, 0, 0, 0, 0, 0,]
property list<var> week_27: [0, 0, 0, 0, 0, 0,]
property list<var> week_28: [0, 0, 0, 0, 0, 0,]
property list<var> week_29: [0, 0, 0, 0, 0, 0,]
property list<var> week_3: [0, 0, 0, 0, 0, 0,]
property list<var> week_30: [0, 0, 0, 0, 0, 0,]
property list<var> week_31: [0, 0, 0, 0, 0, 0,]
property list<var> week_32: [0, 0, 0, 0, 0, 0,]
property list<var> week_33: [0, 0, 0, 0, 0, 0,]
property list<var> week_34: [0, 0, 0, 0, 0, 0,]
property list<var> week_35: [0, 0, 0, 0, 0, 0,]
property list<var> week_36: [0, 0, 0, 0, 0, 0,]
property list<var> week_37: [0, 0, 0, 0, 0, 0,]
property list<var> week_38: [0, 0, 0, 0, 0, 0,]
property list<var> week_39: [0, 0, 0, 0, 0, 0,]
property list<var> week_4: [0, 0, 0, 0, 0, 0,]
property list<var> week_40: [0, 0, 0, 0, 0, 0,]
property list<var> week_41: [0, 0, 0, 0, 0, 0,]
property list<var> week_42: [0, 0, 0, 0, 0, 0,]
property list<var> week_43: [0, 0, 0, 0, 0, 0,]
property list<var> week_44: [0, 0, 0, 0, 0, 0,]
property list<var> week_45: [0, 0, 0, 0, 0, 0,]
property list<var> week_46: [0, 0, 0, 0, 0, 0,]
property list<var> week_47: [0, 0, 0, 0, 0, 0,]
property list<var> week_48: [0, 0, 0, 0, 0, 0,]
property list<var> week_49: [0, 0, 0, 0, 0, 0,]
property list<var> week_5: [0, 0, 0, 0, 0, 0,]
property list<var> week_50: [0, 0, 0, 0, 0, 0,]
property list<var> week_51: [0, 0, 0, 0, 0, 0,]
property list<var> week_6: [0, 0, 0, 0, 0, 0,]
property list<var> week_7: [0, 0, 0, 0, 0, 0,]
property list<var> week_8: [0, 0, 0, 0, 0, 0,]
property list<var> week_9: [0, 0, 0, 0, 0, 0,]
}
+170 -139
View File
@@ -12,68 +12,65 @@ import qs.Daemons
import qs.Helpers
Item {
id: root
id: root
implicitWidth: layout.implicitWidth + 5 * 2
implicitHeight: layout.implicitHeight + 5 * 2
readonly property int topMargin: 0
readonly property int rounding: 6
readonly property int topMargin: 0
required property var wrapper
required property var wrapper
implicitHeight: layout.implicitHeight + 5 * 2
implicitWidth: layout.implicitWidth + 5 * 2
ColumnLayout {
id: layout
ColumnLayout {
id: layout
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
implicitWidth: stack.currentItem ? stack.currentItem.childrenRect.height : 0
spacing: 12
RowLayout {
id: tabBar
spacing: 6
Layout.fillWidth: true
property int tabHeight: 36
Layout.fillWidth: true
spacing: 6
CustomClippingRect {
radius: 6
Layout.fillWidth: true
Layout.preferredHeight: tabBar.tabHeight
color: stack.currentIndex === 0 ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
radius: 6
StateLayer {
function onClicked(): void {
stack.currentIndex = 0;
}
CustomText {
text: qsTr("Volumes")
anchors.centerIn: parent
color: stack.currentIndex === 0 ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3primary
text: qsTr("Volumes")
}
}
}
CustomClippingRect {
radius: 6
Layout.fillWidth: true
Layout.preferredHeight: tabBar.tabHeight
color: stack.currentIndex === 1 ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
radius: 6
StateLayer {
function onClicked(): void {
stack.currentIndex = 1;
}
CustomText {
text: qsTr("Devices")
anchors.centerIn: parent
color: stack.currentIndex === 1 ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3primary
text: qsTr("Devices")
}
}
}
@@ -81,175 +78,245 @@ Item {
StackLayout {
id: stack
Layout.fillWidth: true
Layout.preferredHeight: currentIndex === 0 ? vol.childrenRect.height : dev.childrenRect.height
currentIndex: 0
VolumesTab { id: vol }
DevicesTab { id: dev }
Behavior on currentIndex {
SequentialAnimation {
ParallelAnimation {
Anim {
target: stack
property: "opacity"
to: 0
duration: MaterialEasing.expressiveEffectsTime
property: "opacity"
target: stack
to: 0
}
Anim {
target: stack
property: "scale"
to: 0.9
duration: MaterialEasing.expressiveEffectsTime
property: "scale"
target: stack
to: 0.9
}
}
PropertyAction {}
PropertyAction {
}
ParallelAnimation {
Anim {
target: stack
property: "opacity"
to: 1
duration: MaterialEasing.expressiveEffectsTime
property: "opacity"
target: stack
to: 1
}
Anim {
target: stack
property: "scale"
to: 1
duration: MaterialEasing.expressiveEffectsTime
property: "scale"
target: stack
to: 1
}
}
}
}
}
}
VolumesTab {
id: vol
}
DevicesTab {
id: dev
}
}
}
component DevicesTab: ColumnLayout {
spacing: 12
ButtonGroup {
id: sinks
}
ButtonGroup {
id: sources
}
CustomText {
font.weight: 500
text: qsTr("Output device")
}
Repeater {
model: Audio.sinks
CustomRadioButton {
required property PwNode modelData
ButtonGroup.group: sinks
checked: Audio.sink?.id === modelData.id
text: modelData.description
onClicked: Audio.setAudioSink(modelData)
}
}
CustomText {
Layout.topMargin: 10
font.weight: 500
text: qsTr("Input device")
}
Repeater {
model: Audio.sources
CustomRadioButton {
required property PwNode modelData
ButtonGroup.group: sources
checked: Audio.source?.id === modelData.id
text: modelData.description
onClicked: Audio.setAudioSource(modelData)
}
}
}
component VolumesTab: ColumnLayout {
spacing: 12
CustomRect {
Layout.topMargin: root.topMargin
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2
Layout.fillWidth: true
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2
Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3surfaceContainer
radius: root.rounding
RowLayout {
id: outputVolume
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: Appearance.spacing.smaller
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 15
CustomRect {
Layout.preferredWidth: 40
Layout.preferredHeight: 40
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: 40
Layout.preferredWidth: 40
color: DynamicColors.palette.m3primary
radius: 1000
MaterialIcon {
anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary
text: "speaker"
font.pointSize: 22
text: "speaker"
}
}
ColumnLayout {
Layout.fillWidth: true
RowLayout {
Layout.fillWidth: true
CustomText {
text: "Output Volume"
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillWidth: true
text: "Output Volume"
}
CustomText {
text: qsTr("%1").arg(Audio.muted ? qsTr("Muted") : `${Math.round(Audio.volume * 100)}%`);
font.bold: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
font.bold: true
text: qsTr("%1").arg(Audio.muted ? qsTr("Muted") : `${Math.round(Audio.volume * 100)}%`)
}
}
CustomMouseArea {
Layout.bottomMargin: 5
Layout.fillWidth: true
Layout.preferredHeight: 10
Layout.bottomMargin: 5
CustomSlider {
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: 10
value: Audio.volume
onMoved: Audio.setVolume(value)
Behavior on value { Anim {} }
Behavior on value {
Anim {
}
}
onMoved: Audio.setVolume(value)
}
}
}
}
}
CustomRect {
Layout.topMargin: root.topMargin
Layout.fillWidth: true
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2
Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3surfaceContainer
radius: root.rounding
RowLayout {
id: inputVolume
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: Appearance.spacing.smaller
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 15
Rectangle {
Layout.preferredWidth: 40
Layout.preferredHeight: 40
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: 40
Layout.preferredWidth: 40
color: DynamicColors.palette.m3primary
radius: 1000
MaterialIcon {
anchors.centerIn: parent
anchors.alignWhenCentered: false
anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary
text: "mic"
font.pointSize: 22
text: "mic"
}
}
ColumnLayout {
Layout.fillWidth: true
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.fillWidth: true
CustomText {
text: "Input Volume"
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillWidth: true
text: "Input Volume"
}
CustomText {
text: qsTr("%1").arg(Audio.sourceMuted ? qsTr("Muted") : `${Math.round(Audio.sourceVolume * 100)}%`);
font.bold: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
font.bold: true
text: qsTr("%1").arg(Audio.sourceMuted ? qsTr("Muted") : `${Math.round(Audio.sourceVolume * 100)}%`)
}
}
CustomMouseArea {
Layout.fillWidth: true
Layout.bottomMargin: 5
Layout.fillWidth: true
implicitHeight: 10
CustomSlider {
@@ -257,9 +324,13 @@ Item {
anchors.right: parent.right
implicitHeight: 10
value: Audio.sourceVolume
onMoved: Audio.setSourceVolume(value)
Behavior on value { Anim {} }
Behavior on value {
Anim {
}
}
onMoved: Audio.setSourceVolume(value)
}
}
}
@@ -267,10 +338,9 @@ Item {
}
Rectangle {
Layout.topMargin: root.topMargin
Layout.fillWidth: true
Layout.preferredHeight: 1
Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3outline
}
@@ -280,39 +350,40 @@ Item {
CustomRect {
id: appBox
Layout.topMargin: root.topMargin
required property int index
required property var modelData
Layout.fillWidth: true
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2
Layout.topMargin: root.topMargin
color: DynamicColors.tPalette.m3surfaceContainer
radius: root.rounding
required property var modelData
required property int index
RowLayout {
id: layoutVolume
anchors.fill: parent
anchors.margins: Appearance.spacing.smaller
spacing: 15
CustomRect {
Layout.preferredWidth: 40
Layout.preferredHeight: 40
Layout.alignment: Qt.AlignVCenter
Layout.preferredHeight: 40
Layout.preferredWidth: 40
color: DynamicColors.palette.m3primary
radius: 1000
MaterialIcon {
id: icon
anchors.centerIn: parent
text: "volume_up"
font.pointSize: 22
color: DynamicColors.palette.m3onPrimary
font.pointSize: 22
text: "volume_up"
StateLayer {
radius: 1000
onClicked: {
appBox.modelData.audio.muted = !appBox.modelData.audio.muted;
}
@@ -326,42 +397,46 @@ Item {
TextMetrics {
id: metrics
text: Audio.getStreamName(appBox.modelData)
elide: Text.ElideRight
elideWidth: root.width - 50
text: Audio.getStreamName(appBox.modelData)
}
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.fillWidth: true
CustomText {
text: metrics.elidedText
elide: Text.ElideRight
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillHeight: true
Layout.fillWidth: true
elide: Text.ElideRight
text: metrics.elidedText
}
CustomText {
text: qsTr("%1").arg(appBox.modelData.audio.muted ? qsTr("Muted") : `${Math.round(appBox.modelData.audio.volume * 100)}%`);
font.bold: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
Layout.fillHeight: true
font.bold: true
text: qsTr("%1").arg(appBox.modelData.audio.muted ? qsTr("Muted") : `${Math.round(appBox.modelData.audio.volume * 100)}%`)
}
}
CustomMouseArea {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillHeight: true
Layout.fillWidth: true
implicitHeight: 10
CustomSlider {
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: 10
value: appBox.modelData.audio.volume
onMoved: {
Audio.setStreamVolume(appBox.modelData, value)
Audio.setStreamVolume(appBox.modelData, value);
}
}
}
@@ -370,48 +445,4 @@ Item {
}
}
}
component DevicesTab: ColumnLayout {
spacing: 12
ButtonGroup { id: sinks }
ButtonGroup { id: sources }
CustomText {
text: qsTr("Output device")
font.weight: 500
}
Repeater {
model: Audio.sinks
CustomRadioButton {
required property PwNode modelData
ButtonGroup.group: sinks
checked: Audio.sink?.id === modelData.id
onClicked: Audio.setAudioSink(modelData)
text: modelData.description
}
}
CustomText {
Layout.topMargin: 10
text: qsTr("Input device")
font.weight: 500
}
Repeater {
model: Audio.sources
CustomRadioButton {
required property PwNode modelData
ButtonGroup.group: sources
checked: Audio.source?.id === modelData.id
onClicked: Audio.setAudioSource(modelData)
text: modelData.description
}
}
}
}
+96 -91
View File
@@ -8,113 +8,118 @@ import qs.Config
import qs.Components
Item {
id: root
implicitWidth: expanded ? 300 : 150
anchors.top: parent.top
anchors.bottom: parent.bottom
id: root
property bool expanded: false
property color textColor: DynamicColors.palette.m3onSurface
property color barColor: DynamicColors.palette.m3primary
property color barColor: DynamicColors.palette.m3primary
property bool expanded: false
property color textColor: DynamicColors.palette.m3onSurface
Behavior on implicitWidth {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
}
anchors.bottom: parent.bottom
anchors.top: parent.top
implicitWidth: expanded ? 300 : 150
Rectangle {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.right: parent.right
height: 22
radius: height / 2
color: DynamicColors.tPalette.m3surfaceContainer
Behavior on implicitWidth {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
}
Behavior on color {
CAnim {}
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.tPalette.m3surfaceContainer
height: 22
radius: height / 2
Rectangle {
anchors.centerIn: parent
width: parent.width
height: parent.height
radius: width / 2
color: "transparent"
border.color: "#30ffffff"
border.width: 0
}
Behavior on color {
CAnim {
}
}
RowLayout {
anchors {
fill: parent
leftMargin: 10
rightMargin: 15
}
Rectangle {
anchors.centerIn: parent
border.color: "#30ffffff"
border.width: 0
color: "transparent"
height: parent.height
radius: width / 2
width: parent.width
}
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
font.pixelSize: 18
text: Audio.muted ? "volume_off" : "volume_up"
color: Audio.muted ? DynamicColors.palette.m3error : root.textColor
}
RowLayout {
anchors {
fill: parent
leftMargin: 10
rightMargin: 15
}
Rectangle {
Layout.fillWidth: true
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
color: Audio.muted ? DynamicColors.palette.m3error : root.textColor
font.pixelSize: 18
text: Audio.muted ? "volume_off" : "volume_up"
}
implicitHeight: 4
radius: 20
color: "#50ffffff"
Rectangle {
Layout.fillWidth: true
color: "#50ffffff"
implicitHeight: 4
radius: 20
Rectangle {
id: sinkVolumeBar
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
Rectangle {
id: sinkVolumeBar
implicitWidth: parent.width * ( Audio.volume ?? 0 )
radius: parent.radius
color: Audio.muted ? DynamicColors.palette.m3error : root.barColor
Behavior on color {
CAnim {}
}
}
}
implicitWidth: parent.width * (Audio.volume ?? 0)
radius: parent.radius
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
font.pixelSize: 18
text: Audio.sourceMuted ? "mic_off" : "mic"
color: ( Audio.sourceMuted ?? false ) ? DynamicColors.palette.m3error : root.textColor
}
Behavior on color {
CAnim {
}
}
Rectangle {
Layout.fillWidth: true
anchors {
bottom: parent.bottom
left: parent.left
top: parent.top
}
}
}
implicitHeight: 4
radius: 20
color: "#50ffffff"
MaterialIcon {
Layout.alignment: Qt.AlignVCenter
color: (Audio.sourceMuted ?? false) ? DynamicColors.palette.m3error : root.textColor
font.pixelSize: 18
text: Audio.sourceMuted ? "mic_off" : "mic"
}
Rectangle {
id: sourceVolumeBar
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
Rectangle {
Layout.fillWidth: true
color: "#50ffffff"
implicitHeight: 4
radius: 20
implicitWidth: parent.width * ( Audio.sourceVolume ?? 0 )
radius: parent.radius
color: ( Audio.sourceMuted ?? false ) ? DynamicColors.palette.m3error : root.barColor
Rectangle {
id: sourceVolumeBar
Behavior on color {
CAnim {}
}
}
}
}
}
color: (Audio.sourceMuted ?? false) ? DynamicColors.palette.m3error : root.barColor
implicitWidth: parent.width * (Audio.sourceVolume ?? 0)
radius: parent.radius
Behavior on color {
CAnim {
}
}
anchors {
bottom: parent.bottom
left: parent.left
top: parent.top
}
}
}
}
}
}
+51 -50
View File
@@ -4,64 +4,65 @@ import qs.Components
import qs.Config
ShapePath {
id: root
id: root
required property Wrapper wrapper
required property bool invertBottomRounding
readonly property real rounding: 8
readonly property bool flatten: wrapper.height < rounding * 2
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
property real ibr: invertBottomRounding ? -1 : 1
readonly property bool flatten: wrapper.height < rounding * 2
property real ibr: invertBottomRounding ? -1 : 1
required property bool invertBottomRounding
readonly property real rounding: 8
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
required property Wrapper wrapper
strokeWidth: -1
fillColor: DynamicColors.palette.m3surface
fillColor: DynamicColors.palette.m3surface
strokeWidth: -1
PathArc {
relativeX: root.rounding
relativeY: root.roundingY
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
}
Behavior on fillColor {
CAnim {
}
}
PathLine {
relativeX: 0
relativeY: root.wrapper.height - root.roundingY - root.roundingY * root.ibr
}
PathArc {
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
relativeX: root.rounding
relativeY: root.roundingY
}
PathArc {
relativeX: root.rounding
relativeY: root.roundingY * root.ibr
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
direction: root.invertBottomRounding ? PathArc.Clockwise : PathArc.Counterclockwise
}
PathLine {
relativeX: 0
relativeY: root.wrapper.height - root.roundingY - root.roundingY * root.ibr
}
PathLine {
relativeX: root.wrapper.width - root.rounding * 2
relativeY: 0
}
PathArc {
direction: root.invertBottomRounding ? PathArc.Clockwise : PathArc.Counterclockwise
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
relativeX: root.rounding
relativeY: root.roundingY * root.ibr
}
PathArc {
relativeX: root.rounding
relativeY: -root.roundingY
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
direction: PathArc.Counterclockwise
}
PathLine {
relativeX: root.wrapper.width - root.rounding * 2
relativeY: 0
}
PathLine {
relativeX: 0
relativeY: -( root.wrapper.height - root.roundingY * 2 )
}
PathArc {
direction: PathArc.Counterclockwise
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
relativeX: root.rounding
relativeY: -root.roundingY
}
PathArc {
relativeX: root.rounding
relativeY: -root.roundingY
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
}
PathLine {
relativeX: 0
relativeY: -(root.wrapper.height - root.roundingY * 2)
}
Behavior on fillColor {
CAnim {}
}
PathArc {
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
relativeX: root.rounding
relativeY: -root.roundingY
}
}
+60 -35
View File
@@ -13,24 +13,23 @@ import qs.Modules.Network
RowLayout {
id: root
anchors.fill: parent
readonly property int vPadding: 6
required property Wrapper popouts
required property PersistentProperties visibilities
required property PanelWindow bar
required property Wrapper popouts
required property ShellScreen screen
readonly property int vPadding: 6
required property PersistentProperties visibilities
function checkPopout(x: real): void {
const ch = childAt(x, 2) as WrappedLoader;
if (!ch) {
if ( !popouts.currentName.includes("traymenu") )
if (!popouts.currentName.includes("traymenu"))
popouts.hasCurrent = false;
return;
}
if ( visibilities.sidebar || visibilities.dashboard )
if (visibilities.sidebar || visibilities.dashboard)
return;
const id = ch.id;
@@ -38,49 +37,50 @@ RowLayout {
const item = ch.item;
const itemWidth = item.implicitWidth;
if (id === "audio" && Config.barConfig.popouts.audio) {
popouts.currentName = "audio";
popouts.currentCenter = Qt.binding( () => item.mapToItem(root, itemWidth / 2, 0 ).x );
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
popouts.hasCurrent = true;
} else if ( id === "resources" && Config.barConfig.popouts.resources ) {
} else if (id === "resources" && Config.barConfig.popouts.resources) {
popouts.currentName = "resources";
popouts.currentCenter = Qt.binding( () => item.mapToItem( root, itemWidth / 2, 0 ).x );
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
popouts.hasCurrent = true;
} else if ( id === "tray" && Config.barConfig.popouts.tray ) {
const index = Math.floor((( x - top ) / item.implicitWidth ) * item.items.count );
const trayItem = item.items.itemAt( index );
if ( trayItem ) {
} else if (id === "tray" && Config.barConfig.popouts.tray) {
const index = Math.floor(((x - top) / item.implicitWidth) * item.items.count);
const trayItem = item.items.itemAt(index);
if (trayItem) {
// popouts.currentName = `traymenu${ index }`;
// popouts.currentCenter = Qt.binding( () => trayItem.mapToItem( root, trayItem.implicitWidth / 2, 0 ).x );
// popouts.hasCurrent = true;
} else {
// popouts.hasCurrent = false;
}
} else if ( id === "clock" && Config.barConfig.popouts.clock ) {
} else if (id === "clock" && Config.barConfig.popouts.clock) {
// Calendar.displayYear = new Date().getFullYear();
// Calendar.displayMonth = new Date().getMonth();
// popouts.currentName = "calendar";
// popouts.currentCenter = Qt.binding( () => item.mapToItem( root, itemWidth / 2, 0 ).x );
// popouts.hasCurrent = true;
} else if ( id === "network" && Config.barConfig.popouts.network ) {
} else if (id === "network" && Config.barConfig.popouts.network) {
popouts.currentName = "network";
popouts.currentCenter = Qt.binding( () => item.mapToItem( root, itemWidth / 2, 0 ).x );
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
popouts.hasCurrent = true;
} else if ( id === "upower" && Config.barConfig.popouts.upower ) {
} else if (id === "upower" && Config.barConfig.popouts.upower) {
popouts.currentName = "upower";
popouts.currentCenter = Qt.binding( () => item.mapToItem( root, itemWidth / 2, 0 ).x );
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
popouts.hasCurrent = true;
}
}
anchors.fill: parent
CustomShortcut {
name: "toggle-overview"
onPressed: {
Hyprland.refreshWorkspaces();
Hyprland.refreshMonitors();
if ( root.popouts.hasCurrent && root.popouts.currentName === "overview" ) {
if (root.popouts.hasCurrent && root.popouts.currentName === "overview") {
root.popouts.hasCurrent = false;
} else {
root.popouts.currentName = "overview";
@@ -92,6 +92,7 @@ RowLayout {
Repeater {
id: repeater
model: Config.barConfig.entries
DelegateChooser {
@@ -99,67 +100,87 @@ RowLayout {
DelegateChoice {
roleValue: "spacer"
delegate: WrappedLoader {
Layout.fillWidth: true
}
}
DelegateChoice {
roleValue: "workspaces"
delegate: WrappedLoader {
sourceComponent: Workspaces {
bar: root.bar
}
}
}
DelegateChoice {
roleValue: "audio"
delegate: WrappedLoader {
sourceComponent: AudioWidget {}
sourceComponent: AudioWidget {
}
}
}
DelegateChoice {
roleValue: "tray"
delegate: WrappedLoader {
sourceComponent: TrayWidget {
bar: root.bar
popouts: root.popouts
loader: root
popouts: root.popouts
}
}
}
DelegateChoice {
roleValue: "resources"
delegate: WrappedLoader {
sourceComponent: Resources {}
sourceComponent: Resources {
}
}
}
DelegateChoice {
roleValue: "updates"
delegate: WrappedLoader {
sourceComponent: UpdatesWidget {}
sourceComponent: UpdatesWidget {
}
}
}
DelegateChoice {
roleValue: "notifBell"
delegate: WrappedLoader {
sourceComponent: NotifBell {
visibilities: root.visibilities
popouts: root.popouts
visibilities: root.visibilities
}
}
}
DelegateChoice {
roleValue: "clock"
delegate: WrappedLoader {
sourceComponent: Clock {
loader: root
popouts: root.popouts
visibilities: root.visibilities
loader: root
}
}
}
DelegateChoice {
roleValue: "activeWindow"
delegate: WrappedLoader {
sourceComponent: WindowTitle {
bar: root
@@ -167,16 +188,22 @@ RowLayout {
}
}
}
DelegateChoice {
roleValue: "upower"
delegate: WrappedLoader {
sourceComponent: UPowerWidget {}
sourceComponent: UPowerWidget {
}
}
}
DelegateChoice {
roleValue: "network"
delegate: WrappedLoader {
sourceComponent: NetworkWidget {}
sourceComponent: NetworkWidget {
}
}
}
// DelegateChoice {
@@ -195,15 +222,12 @@ RowLayout {
required property string id
required property int index
Layout.alignment: Qt.AlignVCenter
Layout.fillHeight: true
function findFirstEnabled(): Item {
const count = repeater.count;
for (let i = 0; i < count; i++) {
const item = repeater.itemAt(i);
if (item?.enabled)
return item;
return item;
}
return null;
}
@@ -212,15 +236,16 @@ RowLayout {
for (let i = repeater.count - 1; i >= 0; i--) {
const item = repeater.itemAt(i);
if (item?.enabled)
return item;
return item;
}
return null;
}
Layout.alignment: Qt.AlignVCenter
Layout.fillHeight: true
Layout.leftMargin: findFirstEnabled() === this ? root.vPadding : 0
Layout.rightMargin: findLastEnabled() === this ? root.vPadding : 0
visible: enabled
active: enabled
visible: enabled
}
}
+30 -28
View File
@@ -8,42 +8,44 @@ import qs.Config
import qs.Components
Item {
id: root
id: root
required property Item bar
required property Item bar
required property PersistentProperties visibilities
anchors.fill: parent
anchors.fill: parent
CustomRect {
anchors.fill: parent
color: Config.barConfig.autoHide && !root.visibilities.bar ? "transparent" : DynamicColors.palette.m3surface
CustomRect {
anchors.fill: parent
color: Config.barConfig.autoHide && !root.visibilities.bar ? "transparent" : DynamicColors.palette.m3surface
layer.enabled: true
layer.enabled: true
layer.effect: MultiEffect {
maskEnabled: true
maskInverted: true
maskSource: mask
maskSpreadAtMin: 1
maskThresholdMin: 0.5
}
}
layer.effect: MultiEffect {
maskSource: mask
maskEnabled: true
maskInverted: true
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
}
Item {
id: mask
Item {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
anchors.fill: parent
anchors.topMargin: Config.barConfig.autoHide && !root.visibilities.bar ? 4 : root.bar.implicitHeight
topLeftRadius: 8
topRightRadius: 8
Rectangle {
anchors.fill: parent
anchors.topMargin: Config.barConfig.autoHide && !root.visibilities.bar ? 4 : root.bar.implicitHeight
topLeftRadius: 8
topRightRadius: 8
Behavior on anchors.topMargin {
Anim {}
Anim {
}
}
}
}
}
}
}
+10 -13
View File
@@ -10,17 +10,17 @@ RowLayout {
spacing: 12
Rectangle {
Layout.preferredWidth: 40
Layout.preferredHeight: 40
Layout.preferredWidth: 40
color: "transparent"
radius: 1000
MaterialIcon {
anchors.centerIn: parent
text: "arrow_back_2"
color: DynamicColors.palette.m3onSurface
fill: 1
font.pointSize: 24
color: DynamicColors.palette.m3onSurface
text: "arrow_back_2"
}
StateLayer {
@@ -36,30 +36,27 @@ RowLayout {
}
CustomText {
text: new Date(Calendar.displayYear, Calendar.displayMonth, 1).toLocaleDateString(
Qt.locale(),
"MMMM yyyy"
)
font.weight: 600
font.pointSize: 14
color: DynamicColors.palette.m3onSurface
Layout.fillWidth: true
color: DynamicColors.palette.m3onSurface
font.pointSize: 14
font.weight: 600
horizontalAlignment: Text.AlignHCenter
text: new Date(Calendar.displayYear, Calendar.displayMonth, 1).toLocaleDateString(Qt.locale(), "MMMM yyyy")
}
Rectangle {
Layout.preferredWidth: 40
Layout.preferredHeight: 40
Layout.preferredWidth: 40
color: "transparent"
radius: 1000
MaterialIcon {
anchors.centerIn: parent
text: "arrow_back_2"
color: DynamicColors.palette.m3onSurface
fill: 1
font.pointSize: 24
rotation: 180
color: DynamicColors.palette.m3onSurface
text: "arrow_back_2"
}
StateLayer {
+6 -6
View File
@@ -11,8 +11,8 @@ Item {
required property Item wrapper
implicitWidth: layout.childrenRect.width + layout.anchors.margins * 2
implicitHeight: layout.childrenRect.height + layout.anchors.margins * 2
implicitWidth: layout.childrenRect.width + layout.anchors.margins * 2
ColumnLayout {
id: layout
@@ -59,17 +59,17 @@ Item {
DayOfWeekRow {
id: dayOfWeekRow
locale: Qt.locale()
Layout.fillWidth: true
Layout.preferredHeight: 30
locale: Qt.locale()
}
MonthGrid {
locale: Qt.locale()
wrapper: root.wrapper
Layout.preferredWidth: childrenRect.width
Layout.preferredHeight: childrenRect.height
Layout.preferredWidth: childrenRect.width
locale: Qt.locale()
wrapper: root.wrapper
}
}
}

Some files were not shown because too many files have changed in this diff Show More