mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 23:09:26 -05:00
Compare commits
249 Commits
main
...
hefty-hype
| Author | SHA1 | Date | |
|---|---|---|---|
| 45c6d8a837 | |||
| ad7a7fe566 | |||
| 21689e1d51 | |||
| 74e4e698d9 | |||
| 20d9f80336 | |||
| 4420a6c22d | |||
| bac0b388ad | |||
| 4c590874e0 | |||
| da47151345 | |||
| 9279a5a181 | |||
| a9dcaf0d4b | |||
| a23d050df5 | |||
| 7aa9e603d5 | |||
| b9f668e106 | |||
| b9acc518e9 | |||
| 464cddad00 | |||
| 283995ca99 | |||
| ef9042838e | |||
| 87939110bf | |||
| a62e64e2ef | |||
| 493fb9b686 | |||
| 0db4f8bd5d | |||
| 15002949ee | |||
| 74b869c30b | |||
| a4d45f04f2 | |||
| d87fbf5523 | |||
| c582ecf8af | |||
| 6050148835 | |||
| cc582fd113 | |||
| c8eb9d2830 | |||
| 35c272c807 | |||
| e552776670 | |||
| fdcdc7f1a3 | |||
| 566e100623 | |||
| 2674382b5f | |||
| 01e2eb433b | |||
| 2c30b28fb6 | |||
| 7c27da5336 | |||
| 32934200e4 | |||
| 906d7adf3e | |||
| 37c3fface4 | |||
| ce52695746 | |||
| e597928a9e | |||
| e5def85946 | |||
| f5ae1d360f | |||
| 7f64e5c756 | |||
| 013e81f2ac | |||
| 0b2dfb1910 | |||
| 2eb5885b56 | |||
| beac11dc18 | |||
| 698692a500 | |||
| ef38057830 | |||
| c854d88ee9 | |||
| e1b79693e3 | |||
| 30583abf36 | |||
| 12d510e9eb | |||
| 61a2881ac1 | |||
| 96c8a63b21 | |||
| 950356e47d | |||
| 8cc6087744 | |||
| d52fbe0b40 | |||
| 193d82847a | |||
| 725b0ef5cf | |||
| b1b96904a9 | |||
| 82ed7498c8 | |||
| 1e0ed08909 | |||
| 350f48a74b | |||
| 36f01b78fe | |||
| 3f77086b93 | |||
| 87f61b9331 | |||
| 725c873ab9 | |||
| f02bcdcce7 | |||
| 6b7a12fc9f | |||
| 6878368d71 | |||
| 467f19b40e | |||
| 90c3b642b2 | |||
| 6e433e4a39 | |||
| 05eb696ead | |||
| 0818a6ad9c | |||
| 8ca3ef773a | |||
| e2aa71b59e | |||
| e4ad01c20f | |||
| 522bb5dc0a | |||
| 80752b7812 | |||
| 247da26c7b | |||
| 09d92aff7b | |||
| 3bb9127e32 | |||
| a316f91e86 | |||
| b4c8a63d1a | |||
| 68a51b79ff | |||
| 8d1b9a2ea0 | |||
| ace263cd45 | |||
| 134bf4d986 | |||
| 481e848a65 | |||
| 17c2702d2b | |||
| 84d7928518 | |||
| a81f52fcd7 | |||
| 6eda3c674d | |||
| 54f4709d5f | |||
| 7a46a5c4e5 | |||
| 25dba608ae | |||
| 6eb547bb18 | |||
| cd1a0b3d69 | |||
| 842b09c548 | |||
| f7998dd7c4 | |||
| 43177b9cf3 | |||
| ef86c64933 | |||
| bd767f140f | |||
| 7215d465ea | |||
| 67a19bedaf | |||
| e3dbaf4242 | |||
| 1fe219e215 | |||
| e012eb646c | |||
| 485372ee56 | |||
| b14e0c494c | |||
| 1ad259ff9b | |||
| 2109cf5e09 | |||
| a33122cd3c | |||
| 7a4468258b | |||
| ae0c9f4731 | |||
| 521f3cab6d | |||
| 455252dff1 | |||
| 60ba555de6 | |||
| 485c40406d | |||
| 98a766608d | |||
| 026a660aa7 | |||
| 9e76e89b94 | |||
| da5b8e8912 | |||
| bdf91e1d16 | |||
| 139e64c28e | |||
| 2d94bace7b | |||
| e7368bdc25 | |||
| 09cdad1554 | |||
| 8b84537579 | |||
| ea35ca1582 | |||
| 8fccfef9f1 | |||
| 9e6c1d7c08 | |||
| aaff9d5273 | |||
| 6f633122ed | |||
| 24392e3791 | |||
| 0e049db304 | |||
| 680d8e85c8 | |||
| 6ac1861e1e | |||
| 7f59246c6d | |||
| f3e26d8f22 | |||
| 0a185efcc5 | |||
| 124ea7b245 | |||
| 4fcc12444f | |||
| aefc88755a | |||
| 216a44274e | |||
| db7caae4b5 | |||
| c4f9b20b23 | |||
| 17a99ec068 | |||
| 1225c014e8 | |||
| 5ac40d5445 | |||
| 08b7b393cb | |||
| 0c69a344d5 | |||
| 3ca94d1e70 | |||
| c538505f94 | |||
| 3cac4206f9 | |||
| b3d15cbae1 | |||
| 495e205934 | |||
| ad922ac368 | |||
| 6393092e63 | |||
| e0f2875141 | |||
| 83dcb349da | |||
| 675e14e338 | |||
| 79fe2651cc | |||
| da7bddf1d1 | |||
| a66d0f3146 | |||
| cb5966de0f | |||
| d0e51ffe19 | |||
| 32e8f90b14 | |||
| b59c3f5c30 | |||
| f9bd67699c | |||
| e3eeff8d5d | |||
| 0b0693b1fd | |||
| 227d822655 | |||
| 5e16f2bc10 | |||
| be87a1ae79 | |||
| 653dc9c95f | |||
| e2805ef1a1 | |||
| 6111956c4a | |||
| ff1dfedc72 | |||
| 4e162bd8a6 | |||
| 6bd6f30a5e | |||
| 6952d89a7f | |||
| d1dc89b9f2 | |||
| 1113b6162c | |||
| d41cda858c | |||
| 38dbc8769b | |||
| 7a9b080616 | |||
| a174ed1a84 | |||
| 245aae965f | |||
| 3d5c43135a | |||
| 96b8af05cc | |||
| 422db1ee91 | |||
| 8e21d8c8e3 | |||
| 3b13d0c938 | |||
| f8840e8389 | |||
| 3a4804653d | |||
| dd0b8a5114 | |||
| 6b08e16222 | |||
| 233a06a4c0 | |||
| 2a714378c9 | |||
| e480477db2 | |||
| 83baff7894 | |||
| 3da23ce176 | |||
| 635d49cad0 | |||
| 9ffe4dfb11 | |||
| c155bde816 | |||
| 6cd2d31c99 | |||
| 7a74897b47 | |||
| 28936dd226 | |||
| aa4c18b86a | |||
| 0235e56af3 | |||
| 6b29e73ab7 | |||
| 2bb001a62b | |||
| 15afa07b14 | |||
| bb10002976 | |||
| 368df5b717 | |||
| 6f5ab232a6 | |||
| 3447198e13 | |||
| 1c295ddaac | |||
| 7ba30cee4a | |||
| 6fd2bd51ad | |||
| aec849c820 | |||
| be4eb16b60 | |||
| 314b0ab3d0 | |||
| 10491f2362 | |||
| c2f65e79bc | |||
| 6bb0c251a9 | |||
| e1913d0e95 | |||
| 74c012c930 | |||
| 74368ad25a | |||
| 4718922c55 | |||
| d8eb55fea9 | |||
| 5f9fe2d250 | |||
| 4a44994168 | |||
| 879111fe01 | |||
| ed8c8ae8d7 | |||
| ead98b98b8 | |||
| 5bb1aa06af | |||
| 58184f5be8 | |||
| 28e580c2b1 | |||
| 819fa81fc6 | |||
| fb13cc1a21 | |||
| d1988bef02 | |||
| 7872fba6fe |
@@ -1,160 +1,172 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
|
|
||||||
Scope {
|
Scope {
|
||||||
id: root
|
id: root
|
||||||
property bool failed;
|
property bool failed
|
||||||
property string errorString;
|
property string errorString
|
||||||
|
property real progressHeight: 3
|
||||||
|
|
||||||
// Connect to the Quickshell global to listen for the reload signals.
|
// Connect to the Quickshell global to listen for the reload signals.
|
||||||
Connections {
|
Connections {
|
||||||
target: Quickshell
|
target: Quickshell
|
||||||
|
|
||||||
function onReloadCompleted() {
|
function onReloadCompleted() {
|
||||||
root.failed = false;
|
root.failed = false;
|
||||||
popupLoader.loading = true;
|
popupLoader.loading = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onReloadFailed(error: string) {
|
function onReloadFailed(error: string) {
|
||||||
// Close any existing popup before making a new one.
|
// Close any existing popup before making a new one.
|
||||||
popupLoader.active = false;
|
popupLoader.active = false;
|
||||||
|
|
||||||
root.failed = true;
|
root.failed = true;
|
||||||
root.errorString = error;
|
root.errorString = error;
|
||||||
popupLoader.loading = true;
|
popupLoader.loading = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep the popup in a loader because it isn't needed most of the time
|
// Keep the popup in a loader because it isn't needed most of the time
|
||||||
LazyLoader {
|
LazyLoader {
|
||||||
id: popupLoader
|
id: popupLoader
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: popup
|
id: popup
|
||||||
|
|
||||||
exclusiveZone: 0
|
exclusiveZone: 0
|
||||||
anchors.top: true
|
anchors.top: true
|
||||||
margins.top: 0
|
margins.top: 0
|
||||||
|
|
||||||
implicitWidth: rect.width + shadow.radius * 2
|
implicitWidth: rect.width + 8 * 2
|
||||||
implicitHeight: rect.height + shadow.radius * 2
|
implicitHeight: rect.height + 8 * 2
|
||||||
|
|
||||||
WlrLayershell.namespace: "quickshell:reloadPopup"
|
WlrLayershell.namespace: "quickshell:reloadPopup"
|
||||||
|
|
||||||
// color blending is a bit odd as detailed in the type reference.
|
// color blending is a bit odd as detailed in the type reference.
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
Rectangle {
|
RectangularShadow {
|
||||||
id: rect
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: failed ? "#ffe99195" : "#ffD1E8D5"
|
|
||||||
|
|
||||||
implicitHeight: layout.implicitHeight + 30
|
|
||||||
implicitWidth: layout.implicitWidth + 30
|
|
||||||
radius: 12
|
|
||||||
|
|
||||||
// Fills the whole area of the rectangle, making any clicks go to it,
|
|
||||||
// which dismiss the popup.
|
|
||||||
MouseArea {
|
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
onPressed: {
|
|
||||||
popupLoader.active = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// makes the mouse area track mouse hovering, so the hide animation
|
|
||||||
// can be paused when hovering.
|
|
||||||
hoverEnabled: true
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: layout
|
|
||||||
spacing: 10
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
topMargin: 10
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
renderType: Text.NativeRendering
|
|
||||||
font.family: "Google Sans Flex"
|
|
||||||
font.pointSize: 14
|
|
||||||
text: root.failed ? "Quickshell: Reload failed" : "Quickshell reloaded"
|
|
||||||
color: failed ? "#ff93000A" : "#ff0C1F13"
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
renderType: Text.NativeRendering
|
|
||||||
font.family: "JetBrains Mono NF"
|
|
||||||
font.pointSize: 11
|
|
||||||
text: root.errorString
|
|
||||||
color: failed ? "#ff93000A" : "#ff0C1F13"
|
|
||||||
// When visible is false, it also takes up no space.
|
|
||||||
visible: root.errorString != ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A progress bar on the bottom of the screen, showing how long until the
|
|
||||||
// popup is removed.
|
|
||||||
Rectangle {
|
|
||||||
z: 2
|
|
||||||
id: bar
|
|
||||||
color: failed ? "#ff93000A" : "#ff0C1F13"
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.margins: 10
|
|
||||||
height: 5
|
|
||||||
radius: 9999
|
|
||||||
|
|
||||||
PropertyAnimation {
|
|
||||||
id: anim
|
|
||||||
target: bar
|
|
||||||
property: "width"
|
|
||||||
from: rect.width - bar.anchors.margins * 2
|
|
||||||
to: 0
|
|
||||||
duration: failed ? 10000 : 1000
|
|
||||||
onFinished: popupLoader.active = false
|
|
||||||
|
|
||||||
// Pause the animation when the mouse is hovering over the popup,
|
|
||||||
// so it stays onscreen while reading. This updates reactively
|
|
||||||
// when the mouse moves on and off the popup.
|
|
||||||
paused: mouseArea.containsMouse
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Its bg
|
|
||||||
Rectangle {
|
|
||||||
z: 1
|
|
||||||
id: bar_bg
|
|
||||||
color: failed ? "#30af1b25" : "#4027643e"
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.margins: 10
|
|
||||||
height: 5
|
|
||||||
radius: 9999
|
|
||||||
width: rect.width - bar.anchors.margins * 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// We could set `running: true` inside the animation, but the width of the
|
|
||||||
// rectangle might not be calculated yet, due to the layout.
|
|
||||||
// In the `Component.onCompleted` event handler, all of the component's
|
|
||||||
// properties and children have been initialized.
|
|
||||||
Component.onCompleted: anim.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
DropShadow {
|
|
||||||
id: shadow
|
|
||||||
anchors.fill: rect
|
anchors.fill: rect
|
||||||
horizontalOffset: 0
|
radius: rect.radius
|
||||||
verticalOffset: 2
|
blur: 6.3
|
||||||
radius: 6
|
offset: Qt.vector2d(0.0, 1.0)
|
||||||
samples: radius * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs
|
spread: 1
|
||||||
color: "#44000000"
|
color: "#55000000"
|
||||||
source: rect
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
Rectangle {
|
||||||
|
id: rect
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: root.failed ? "#ffe99195" : "#ffD1E8D5"
|
||||||
|
|
||||||
|
implicitHeight: layout.implicitHeight + 30
|
||||||
|
implicitWidth: layout.implicitWidth + 30
|
||||||
|
radius: 8
|
||||||
|
|
||||||
|
// Fills the whole area of the rectangle, making any clicks go to it,
|
||||||
|
// which dismiss the popup.
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: {
|
||||||
|
popupLoader.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// makes the mouse area track mouse hovering, so the hide animation
|
||||||
|
// can be paused when hovering.
|
||||||
|
hoverEnabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
spacing: 10
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 10
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: title
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
font.family: "Google Sans Flex"
|
||||||
|
font.pointSize: 14
|
||||||
|
text: root.failed ? "Quickshell: Reload failed" : "Quickshell reloaded"
|
||||||
|
color: root.failed ? "#ff93000A" : "#ff0C1F13"
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: info
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
font.family: "JetBrains Mono NF"
|
||||||
|
font.pointSize: 11
|
||||||
|
text: root.errorString
|
||||||
|
color: root.failed ? "#ff93000A" : "#ff0C1F13"
|
||||||
|
// When visible is false, it also takes up no space.
|
||||||
|
visible: root.errorString != ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A progress bar on the bottom of the screen, showing how long until the
|
||||||
|
// popup is removed.
|
||||||
|
Rectangle {
|
||||||
|
id: bar
|
||||||
|
z: 2
|
||||||
|
color: root.failed ? "#ff93000A" : "#ff0C1F13"
|
||||||
|
property real maxWidth: Math.max(title.width, info.width)
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: (parent.width - maxWidth) / 2
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: 10
|
||||||
|
}
|
||||||
|
height: root.progressHeight
|
||||||
|
radius: 9999
|
||||||
|
|
||||||
|
PropertyAnimation {
|
||||||
|
id: anim
|
||||||
|
target: bar
|
||||||
|
property: "width"
|
||||||
|
from: Math.max(title.width, info.width)
|
||||||
|
to: 0
|
||||||
|
duration: root.failed ? 10000 : 1000
|
||||||
|
onFinished: popupLoader.active = false
|
||||||
|
|
||||||
|
// Pause the animation when the mouse is hovering over the popup,
|
||||||
|
// so it stays onscreen while reading. This updates reactively
|
||||||
|
// when the mouse moves on and off the popup.
|
||||||
|
paused: mouseArea.containsMouse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Its bg
|
||||||
|
Rectangle {
|
||||||
|
id: bar_bg
|
||||||
|
z: 1
|
||||||
|
color: root.failed ? "#30af1b25" : "#4027643e"
|
||||||
|
property real maxWidth: Math.max(title.width, info.width)
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: (parent.width - maxWidth) / 2
|
||||||
|
rightMargin: anchors.leftMargin
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: 10
|
||||||
|
}
|
||||||
|
height: root.progressHeight
|
||||||
|
radius: 9999
|
||||||
|
width: bar.width
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could set `running: true` inside the animation, but the width of the
|
||||||
|
// rectangle might not be calculated yet, due to the layout.
|
||||||
|
// In the `Component.onCompleted` event handler, all of the component's
|
||||||
|
// properties and children have been initialized.
|
||||||
|
Component.onCompleted: anim.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
property QtObject variableAxes: QtObject {
|
property QtObject variableAxes: QtObject {
|
||||||
property var main: ({
|
property var main: ({
|
||||||
"wght": 450,
|
"wght": 500,
|
||||||
"wdth": 100,
|
"wdth": 100,
|
||||||
})
|
})
|
||||||
property var numbers: ({
|
property var numbers: ({
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ pragma ComponentBehavior: Bound
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.modules.common.functions
|
|
||||||
|
import "functions"
|
||||||
|
import "config"
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
@@ -13,6 +15,8 @@ Singleton {
|
|||||||
property int readWriteDelay: 50 // milliseconds
|
property int readWriteDelay: 50 // milliseconds
|
||||||
property bool blockWrites: false
|
property bool blockWrites: false
|
||||||
|
|
||||||
|
signal reloaded()
|
||||||
|
|
||||||
function setNestedValue(nestedKey, value) {
|
function setNestedValue(nestedKey, value) {
|
||||||
let keys = nestedKey.split(".");
|
let keys = nestedKey.split(".");
|
||||||
let obj = root.options;
|
let obj = root.options;
|
||||||
@@ -48,7 +52,7 @@ Singleton {
|
|||||||
interval: root.readWriteDelay
|
interval: root.readWriteDelay
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
configFileView.reload()
|
configFileView.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +61,7 @@ Singleton {
|
|||||||
interval: root.readWriteDelay
|
interval: root.readWriteDelay
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
configFileView.writeAdapter()
|
configFileView.writeAdapter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +72,10 @@ Singleton {
|
|||||||
blockWrites: root.blockWrites
|
blockWrites: root.blockWrites
|
||||||
onFileChanged: fileReloadTimer.restart()
|
onFileChanged: fileReloadTimer.restart()
|
||||||
onAdapterUpdated: fileWriteTimer.restart()
|
onAdapterUpdated: fileWriteTimer.restart()
|
||||||
onLoaded: root.ready = true
|
onLoaded: {
|
||||||
|
if (!root.ready) root.reloaded()
|
||||||
|
root.ready = true
|
||||||
|
}
|
||||||
onLoadFailed: error => {
|
onLoadFailed: error => {
|
||||||
if (error == FileViewError.FileNotFound) {
|
if (error == FileViewError.FileNotFound) {
|
||||||
writeAdapter();
|
writeAdapter();
|
||||||
@@ -152,8 +159,8 @@ Singleton {
|
|||||||
property JsonObject apps: JsonObject {
|
property JsonObject apps: JsonObject {
|
||||||
property string bluetooth: "kcmshell6 kcm_bluetooth"
|
property string bluetooth: "kcmshell6 kcm_bluetooth"
|
||||||
property string changePassword: "kitty -1 --hold=yes fish -i -c 'passwd'"
|
property string changePassword: "kitty -1 --hold=yes fish -i -c 'passwd'"
|
||||||
property string network: "kcmshell6 kcm_networkmanagement"
|
|
||||||
property string manageUser: "kcmshell6 kcm_users"
|
property string manageUser: "kcmshell6 kcm_users"
|
||||||
|
property string network: "kcmshell6 kcm_networkmanagement"
|
||||||
property string networkEthernet: "kcmshell6 kcm_networkmanagement"
|
property string networkEthernet: "kcmshell6 kcm_networkmanagement"
|
||||||
property string taskManager: "plasma-systemmonitor --page-name Processes"
|
property string taskManager: "plasma-systemmonitor --page-name Processes"
|
||||||
property string terminal: "kitty -1" // This is only for shell actions
|
property string terminal: "kitty -1" // This is only for shell actions
|
||||||
@@ -238,9 +245,15 @@ Singleton {
|
|||||||
property bool floatStyleShadow: true // Show shadow behind bar when cornerStyle == 1 (Float)
|
property bool floatStyleShadow: true // Show shadow behind bar when cornerStyle == 1 (Float)
|
||||||
property bool borderless: false // true for no grouping of items
|
property bool borderless: false // true for no grouping of items
|
||||||
property string topLeftIcon: "spark" // Options: "distro" or any icon name in ~/.config/quickshell/ii/assets/icons
|
property string topLeftIcon: "spark" // Options: "distro" or any icon name in ~/.config/quickshell/ii/assets/icons
|
||||||
|
property list<string> screenList: [] // List of names, like "eDP-1", find out with 'hyprctl monitors' command
|
||||||
property bool showBackground: true
|
property bool showBackground: true
|
||||||
property bool verbose: true
|
property bool verbose: true
|
||||||
property bool vertical: false
|
property bool vertical: false
|
||||||
|
property JsonObject indicators: JsonObject {
|
||||||
|
property JsonObject notifications: JsonObject {
|
||||||
|
property bool showUnreadCount: false
|
||||||
|
}
|
||||||
|
}
|
||||||
property JsonObject resources: JsonObject {
|
property JsonObject resources: JsonObject {
|
||||||
property bool alwaysShowSwap: true
|
property bool alwaysShowSwap: true
|
||||||
property bool alwaysShowCpu: true
|
property bool alwaysShowCpu: true
|
||||||
@@ -248,7 +261,9 @@ Singleton {
|
|||||||
property int swapWarningThreshold: 85
|
property int swapWarningThreshold: 85
|
||||||
property int cpuWarningThreshold: 90
|
property int cpuWarningThreshold: 90
|
||||||
}
|
}
|
||||||
property list<string> screenList: [] // List of names, like "eDP-1", find out with 'hyprctl monitors' command
|
property JsonObject tooltips: JsonObject {
|
||||||
|
property bool clickToShow: false
|
||||||
|
}
|
||||||
property JsonObject utilButtons: JsonObject {
|
property JsonObject utilButtons: JsonObject {
|
||||||
property bool showScreenSnip: true
|
property bool showScreenSnip: true
|
||||||
property bool showColorPicker: false
|
property bool showColorPicker: false
|
||||||
@@ -258,6 +273,13 @@ Singleton {
|
|||||||
property bool showPerformanceProfileToggle: false
|
property bool showPerformanceProfileToggle: false
|
||||||
property bool showScreenRecord: false
|
property bool showScreenRecord: false
|
||||||
}
|
}
|
||||||
|
property JsonObject weather: JsonObject {
|
||||||
|
property bool enable: false
|
||||||
|
property bool enableGPS: true // gps based location
|
||||||
|
property string city: "" // When 'enableGPS' is false
|
||||||
|
property bool useUSCS: false // Instead of metric (SI) units
|
||||||
|
property int fetchInterval: 10 // minutes
|
||||||
|
}
|
||||||
property JsonObject workspaces: JsonObject {
|
property JsonObject workspaces: JsonObject {
|
||||||
property bool monochromeIcons: true
|
property bool monochromeIcons: true
|
||||||
property int shown: 10
|
property int shown: 10
|
||||||
@@ -267,21 +289,6 @@ Singleton {
|
|||||||
property list<string> numberMap: ["1", "2"] // Characters to show instead of numbers on workspace indicator
|
property list<string> numberMap: ["1", "2"] // Characters to show instead of numbers on workspace indicator
|
||||||
property bool useNerdFont: false
|
property bool useNerdFont: false
|
||||||
}
|
}
|
||||||
property JsonObject weather: JsonObject {
|
|
||||||
property bool enable: false
|
|
||||||
property bool enableGPS: true // gps based location
|
|
||||||
property string city: "" // When 'enableGPS' is false
|
|
||||||
property bool useUSCS: false // Instead of metric (SI) units
|
|
||||||
property int fetchInterval: 10 // minutes
|
|
||||||
}
|
|
||||||
property JsonObject indicators: JsonObject {
|
|
||||||
property JsonObject notifications: JsonObject {
|
|
||||||
property bool showUnreadCount: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
property JsonObject tooltips: JsonObject {
|
|
||||||
property bool clickToShow: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject battery: JsonObject {
|
property JsonObject battery: JsonObject {
|
||||||
@@ -293,7 +300,10 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject calendar: JsonObject {
|
property JsonObject calendar: JsonObject {
|
||||||
property string locale: "en-GB"
|
property string locale: "C"
|
||||||
|
property bool force2CharDayOfWeek: true
|
||||||
|
property bool animate: false // Disabled by default cuz laggy
|
||||||
|
property bool weekScrollPrecision: false // One scroll advances 1 week instead of 1 month
|
||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject cheatsheet: JsonObject {
|
property JsonObject cheatsheet: JsonObject {
|
||||||
@@ -341,7 +351,8 @@ Singleton {
|
|||||||
property int mouseScrollFactor: 120
|
property int mouseScrollFactor: 120
|
||||||
property int touchpadScrollFactor: 450
|
property int touchpadScrollFactor: 450
|
||||||
}
|
}
|
||||||
property JsonObject deadPixelWorkaround: JsonObject { // Hyprland leaves out 1 pixel on the right for interactions
|
property JsonObject deadPixelWorkaround: JsonObject {
|
||||||
|
// Hyprland leaves out 1 pixel on the right for interactions
|
||||||
property bool enable: false
|
property bool enable: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,7 +367,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject launcher: JsonObject {
|
property JsonObject launcher: JsonObject {
|
||||||
property list<string> pinnedApps: [ "org.kde.dolphin", "kitty", "cmake-gui"]
|
property list<string> pinnedApps: ["org.kde.dolphin", "kitty", "cmake-gui"]
|
||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject light: JsonObject {
|
property JsonObject light: JsonObject {
|
||||||
@@ -399,7 +410,7 @@ Singleton {
|
|||||||
|
|
||||||
property JsonObject notifications: JsonObject {
|
property JsonObject notifications: JsonObject {
|
||||||
property int timeout: 7000
|
property int timeout: 7000
|
||||||
property JsonObject monitor: JsonObject {
|
property JsonObject forceMonitor: JsonObject {
|
||||||
property bool enable: false
|
property bool enable: false
|
||||||
property string name: "" // Name of the monitor to show notifications on, like "eDP-1". Find out with 'hyprctl monitors' command
|
property string name: "" // Name of the monitor to show notifications on, like "eDP-1". Find out with 'hyprctl monitors' command
|
||||||
}
|
}
|
||||||
@@ -465,7 +476,7 @@ Singleton {
|
|||||||
property bool monochromeIcons: true
|
property bool monochromeIcons: true
|
||||||
property bool showItemId: false
|
property bool showItemId: false
|
||||||
property bool invertPinnedItems: true // Makes the below a whitelist for the tray and blacklist for the pinned area
|
property bool invertPinnedItems: true // Makes the below a whitelist for the tray and blacklist for the pinned area
|
||||||
property list<var> pinnedItems: [ "Fcitx" ]
|
property list<var> pinnedItems: ["Fcitx"]
|
||||||
property bool filterPassive: true
|
property bool filterPassive: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,12 +540,30 @@ Singleton {
|
|||||||
property JsonObject android: JsonObject {
|
property JsonObject android: JsonObject {
|
||||||
property int columns: 5
|
property int columns: 5
|
||||||
property list<var> toggles: [
|
property list<var> toggles: [
|
||||||
{ "size": 2, "type": "network" },
|
{
|
||||||
{ "size": 2, "type": "bluetooth" },
|
"size": 2,
|
||||||
{ "size": 1, "type": "idleInhibitor" },
|
"type": "network"
|
||||||
{ "size": 1, "type": "mic" },
|
},
|
||||||
{ "size": 2, "type": "audio" },
|
{
|
||||||
{ "size": 2, "type": "nightLight" }
|
"size": 2,
|
||||||
|
"type": "bluetooth"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 1,
|
||||||
|
"type": "idleInhibitor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 1,
|
||||||
|
"type": "mic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 2,
|
||||||
|
"type": "audio"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 2,
|
||||||
|
"type": "nightLight"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -548,7 +577,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject screenRecord: JsonObject {
|
property JsonObject screenRecord: JsonObject {
|
||||||
property string savePath: Directories.videos.replace("file://","") // strip "file://"
|
property string savePath: Directories.videos.replace("file://", "") // strip "file://"
|
||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject screenSnip: JsonObject {
|
property JsonObject screenSnip: JsonObject {
|
||||||
@@ -608,26 +637,8 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject waffles: JsonObject {
|
property JsonObject hefty: HeftyConfig {}
|
||||||
// Some spots are kinda janky/awkward. Setting the following to
|
property JsonObject waffles: WaffleConfig {}
|
||||||
// false will make (some) stuff also be like that for accuracy.
|
|
||||||
// Example: the right-click menu of the Start button
|
|
||||||
property JsonObject tweaks: JsonObject {
|
|
||||||
property bool switchHandlePositionFix: true
|
|
||||||
property bool smootherMenuAnimations: true
|
|
||||||
property bool smootherSearchBar: true
|
|
||||||
}
|
|
||||||
property JsonObject bar: JsonObject {
|
|
||||||
property bool bottom: true
|
|
||||||
property bool leftAlignApps: false
|
|
||||||
}
|
|
||||||
property JsonObject actionCenter: JsonObject {
|
|
||||||
property list<string> toggles: [ "network", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "nightLight", "darkMode", "antiFlashbang", "cloudflareWarp", "mic", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ]
|
|
||||||
}
|
|
||||||
property JsonObject calendar: JsonObject {
|
|
||||||
property bool force2CharDayOfWeek: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ Singleton {
|
|||||||
readonly property string music: StandardPaths.standardLocations(StandardPaths.MusicLocation)[0]
|
readonly property string music: StandardPaths.standardLocations(StandardPaths.MusicLocation)[0]
|
||||||
readonly property string videos: StandardPaths.standardLocations(StandardPaths.MoviesLocation)[0]
|
readonly property string videos: StandardPaths.standardLocations(StandardPaths.MoviesLocation)[0]
|
||||||
|
|
||||||
// Other dirs used by the shell, without "file://"
|
/////// Stuff below are without "file://" /////////
|
||||||
|
// General
|
||||||
property string assetsPath: Quickshell.shellPath("assets")
|
property string assetsPath: Quickshell.shellPath("assets")
|
||||||
property string scriptPath: Quickshell.shellPath("scripts")
|
property string scriptPath: Quickshell.shellPath("scripts")
|
||||||
property string favicons: FileUtils.trimFileProtocol(`${Directories.cache}/media/favicons`)
|
property string favicons: FileUtils.trimFileProtocol(`${Directories.cache}/media/favicons`)
|
||||||
@@ -30,9 +31,6 @@ Singleton {
|
|||||||
property string booruDownloads: FileUtils.trimFileProtocol(Directories.pictures + "/homework")
|
property string booruDownloads: FileUtils.trimFileProtocol(Directories.pictures + "/homework")
|
||||||
property string booruDownloadsNsfw: FileUtils.trimFileProtocol(Directories.pictures + "/homework/🌶️")
|
property string booruDownloadsNsfw: FileUtils.trimFileProtocol(Directories.pictures + "/homework/🌶️")
|
||||||
property string latexOutput: FileUtils.trimFileProtocol(`${Directories.cache}/media/latex`)
|
property string latexOutput: FileUtils.trimFileProtocol(`${Directories.cache}/media/latex`)
|
||||||
property string shellConfig: FileUtils.trimFileProtocol(`${Directories.config}/illogical-impulse`)
|
|
||||||
property string shellConfigName: "config.json"
|
|
||||||
property string shellConfigPath: `${Directories.shellConfig}/${Directories.shellConfigName}`
|
|
||||||
property string todoPath: FileUtils.trimFileProtocol(`${Directories.state}/user/todo.json`)
|
property string todoPath: FileUtils.trimFileProtocol(`${Directories.state}/user/todo.json`)
|
||||||
property string notesPath: FileUtils.trimFileProtocol(`${Directories.state}/user/notes.txt`)
|
property string notesPath: FileUtils.trimFileProtocol(`${Directories.state}/user/notes.txt`)
|
||||||
property string conflictCachePath: FileUtils.trimFileProtocol(`${Directories.cache}/conflict-killer`)
|
property string conflictCachePath: FileUtils.trimFileProtocol(`${Directories.cache}/conflict-killer`)
|
||||||
@@ -43,24 +41,39 @@ Singleton {
|
|||||||
property string screenshotTemp: "/tmp/quickshell/media/screenshot"
|
property string screenshotTemp: "/tmp/quickshell/media/screenshot"
|
||||||
property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/colors/switchwall.sh`)
|
property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/colors/switchwall.sh`)
|
||||||
property string defaultAiPrompts: Quickshell.shellPath("defaults/ai/prompts")
|
property string defaultAiPrompts: Quickshell.shellPath("defaults/ai/prompts")
|
||||||
property string userAiPrompts: FileUtils.trimFileProtocol(`${Directories.shellConfig}/ai/prompts`)
|
|
||||||
property string userActions: FileUtils.trimFileProtocol(`${Directories.shellConfig}/actions`)
|
|
||||||
property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`)
|
property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`)
|
||||||
property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`)
|
property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`)
|
||||||
property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.sh`)
|
property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.sh`)
|
||||||
property string userAvatarPathAccountsService: FileUtils.trimFileProtocol(`/var/lib/AccountsService/icons/${SystemInfo.username}`)
|
property string userAvatarPathAccountsService: FileUtils.trimFileProtocol(`/var/lib/AccountsService/icons/${SystemInfo.username}`)
|
||||||
property string userAvatarPathRicersAndWeirdSystems: FileUtils.trimFileProtocol(`${Directories.home}.face`)
|
property string userAvatarPathRicersAndWeirdSystems: FileUtils.trimFileProtocol(`${Directories.home}.face`)
|
||||||
property string userAvatarPathRicersAndWeirdSystems2: FileUtils.trimFileProtocol(`${Directories.home}.face.icon`)
|
property string userAvatarPathRicersAndWeirdSystems2: FileUtils.trimFileProtocol(`${Directories.home}.face.icon`)
|
||||||
|
|
||||||
|
// User
|
||||||
|
property string shellConfig: FileUtils.trimFileProtocol(`${Directories.config}/illogical-impulse`)
|
||||||
|
property string shellConfigName: "config.json"
|
||||||
|
property string shellConfigPath: `${Directories.shellConfig}/${Directories.shellConfigName}`
|
||||||
|
property string userAiPrompts: FileUtils.trimFileProtocol(`${Directories.shellConfig}/ai/prompts`)
|
||||||
|
property string userActions: FileUtils.trimFileProtocol(`${Directories.shellConfig}/actions`)
|
||||||
|
property string userComponents: FileUtils.trimFileProtocol(`${Directories.shellConfig}/components`)
|
||||||
|
|
||||||
// Cleanup on init
|
// Cleanup on init
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`])
|
|
||||||
Quickshell.execDetached(["mkdir", "-p", `${favicons}`])
|
|
||||||
Quickshell.execDetached(["bash", "-c", `rm -rf '${coverArt}'; mkdir -p '${coverArt}'`])
|
|
||||||
Quickshell.execDetached(["bash", "-c", `rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`])
|
|
||||||
Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`])
|
|
||||||
Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`])
|
|
||||||
Quickshell.execDetached(["mkdir", "-p", `${aiChats}`])
|
Quickshell.execDetached(["mkdir", "-p", `${aiChats}`])
|
||||||
Quickshell.execDetached(["mkdir", "-p", `${userActions}`])
|
Quickshell.execDetached(["mkdir", "-p", `${favicons}`])
|
||||||
|
Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`])
|
||||||
Quickshell.execDetached(["rm", "-rf", `${tempImages}`])
|
Quickshell.execDetached(["rm", "-rf", `${tempImages}`])
|
||||||
|
Quickshell.execDetached(["mkdir", "-p", `${userComponents}`])
|
||||||
|
Quickshell.execDetached(["mkdir", "-p", `${userAiPrompts}`])
|
||||||
|
Quickshell.execDetached(["mkdir", "-p", `${userActions}`])
|
||||||
|
Quickshell.execDetached(["bash", "-c", `rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`])
|
||||||
|
Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`])
|
||||||
|
Quickshell.execDetached(["bash", "-c", `rm -rf '${coverArt}'; mkdir -p '${coverArt}'`])
|
||||||
|
Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`])
|
||||||
|
}
|
||||||
|
Component.onDestruction: {
|
||||||
|
Quickshell.execDetached(["bash", "-c", `rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`])
|
||||||
|
Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`])
|
||||||
|
Quickshell.execDetached(["bash", "-c", `rm -rf '${coverArt}'; mkdir -p '${coverArt}'`])
|
||||||
|
Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
JsonObject {
|
||||||
|
property JsonObject bar: JsonObject {
|
||||||
|
property list<var> leftWidgets: ["HLeftSidebarButton", "HWindowInfo"]
|
||||||
|
property list<var> centerLeftWidgets: ["HTime"]
|
||||||
|
property list<var> centerWidgets: ["HWorkspaces"]
|
||||||
|
property list<var> centerRightWidgets: ["HResources"]
|
||||||
|
property list<var> rightWidgets: ["HSystemTray", "HSystemIndicators"]
|
||||||
|
property bool m3ExpressiveGrouping: true
|
||||||
|
|
||||||
|
property JsonObject resources: JsonObject {
|
||||||
|
property bool showMemory: false
|
||||||
|
property bool showRam: false
|
||||||
|
property bool showSwap: false
|
||||||
|
property bool showCpu: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
JsonObject {
|
||||||
|
// Some spots are kinda janky/awkward. Setting the following to
|
||||||
|
// false will make (some) stuff also be like that for accuracy.
|
||||||
|
// Example: the right-click menu of the Start button
|
||||||
|
property JsonObject tweaks: JsonObject {
|
||||||
|
property bool switchHandlePositionFix: true
|
||||||
|
property bool smootherMenuAnimations: true
|
||||||
|
property bool smootherSearchBar: true
|
||||||
|
}
|
||||||
|
property JsonObject bar: JsonObject {
|
||||||
|
property bool bottom: true
|
||||||
|
property bool leftAlignApps: false
|
||||||
|
}
|
||||||
|
property JsonObject actionCenter: JsonObject {
|
||||||
|
property list<string> toggles: [ "network", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "nightLight", "darkMode", "antiFlashbang", "cloudflareWarp", "mic", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -109,7 +109,8 @@ Singleton {
|
|||||||
*/
|
*/
|
||||||
function transparentize(color, percentage = 1) {
|
function transparentize(color, percentage = 1) {
|
||||||
var c = Qt.color(color);
|
var c = Qt.color(color);
|
||||||
return Qt.rgba(c.r, c.g, c.b, c.a * (1 - percentage));
|
var a = c.a * (1 - clamp01(percentage));
|
||||||
|
return Qt.rgba(c.r, c.g, c.b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,7 +122,7 @@ Singleton {
|
|||||||
*/
|
*/
|
||||||
function applyAlpha(color, alpha) {
|
function applyAlpha(color, alpha) {
|
||||||
var c = Qt.color(color);
|
var c = Qt.color(color);
|
||||||
var a = Math.max(0, Math.min(1, alpha));
|
var a = clamp01(alpha);
|
||||||
return Qt.rgba(c.r, c.g, c.b, a);
|
return Qt.rgba(c.r, c.g, c.b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,4 +24,19 @@ Singleton {
|
|||||||
targetDate.setDate(firstDayDate.getDate() + i);
|
targetDate.setDate(firstDayDate.getDate() + i);
|
||||||
return targetDate;
|
return targetDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatDuration(seconds) {
|
||||||
|
const d = Math.floor(seconds / 86400);
|
||||||
|
const h = Math.floor((seconds % 86400) / 3600);
|
||||||
|
const m = Math.floor((seconds % 3600) / 60);
|
||||||
|
|
||||||
|
let str = "";
|
||||||
|
if (d > 0)
|
||||||
|
str += `${d}d`;
|
||||||
|
if (h > 0)
|
||||||
|
str += `${str ? ", " : ""}${h}h`;
|
||||||
|
if (m > 0 || !str)
|
||||||
|
str += `${str ? ", " : ""}${m}m`;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
pragma Singleton
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds the given number to the nearest even integer.
|
||||||
|
*
|
||||||
|
* @param {number} num - The number to round.
|
||||||
|
* @returns {number} The nearest even integer.
|
||||||
|
*/
|
||||||
|
function roundToEven(num) {
|
||||||
|
return Math.round(num / 2) * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,4 +95,15 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findParentWithProperty(obj, propertyName) {
|
||||||
|
let current = obj;
|
||||||
|
while (current) {
|
||||||
|
if (current.hasOwnProperty(propertyName)) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
current = current.parent;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import QtQuick
|
|||||||
// The former animates faster than the latter, see the NumberAnimations below
|
// The former animates faster than the latter, see the NumberAnimations below
|
||||||
QtObject {
|
QtObject {
|
||||||
id: root
|
id: root
|
||||||
required property int index
|
property int index
|
||||||
|
|
||||||
property real idx1: index
|
property real idx1: index
|
||||||
property real idx2: index
|
property real idx2: index
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common as C
|
||||||
|
|
||||||
|
NestableObject {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property HyprlandMonitor monitor
|
||||||
|
readonly property var liveMonitorData: HyprlandData.monitors.find(m => m.id === monitor.id)
|
||||||
|
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||||
|
readonly property int activeWorkspace: monitor?.activeWorkspace?.id
|
||||||
|
readonly property bool currentWorkspaceNotFake: activeWindow?.activated ?? false // Active empty workspace = fake. At least, that's how I like to call it.
|
||||||
|
readonly property int fakeWorkspace: currentWorkspaceNotFake ? -9999 : activeWorkspace
|
||||||
|
readonly property int shownCount: C.Config.options.bar.workspaces.shown
|
||||||
|
readonly property int group: Math.floor((activeWorkspace - 1) / shownCount)
|
||||||
|
readonly property var specialWorkspace: liveMonitorData?.specialWorkspace
|
||||||
|
readonly property string specialWorkspaceName: specialWorkspace.name.replace("special:", "")
|
||||||
|
readonly property bool specialWorkspaceActive: specialWorkspaceName !== ""
|
||||||
|
|
||||||
|
property list<bool> occupied: []
|
||||||
|
property list<var> biggestWindow: occupied.map((_, index) => {
|
||||||
|
const wsId = getWorkspaceIdAt(index);
|
||||||
|
var biggestWindow = HyprlandData.biggestWindowForWorkspace(wsId);
|
||||||
|
return biggestWindow;
|
||||||
|
})
|
||||||
|
|
||||||
|
function getWorkspaceId(group, index) {
|
||||||
|
return group * root.shownCount + index + 1;
|
||||||
|
}
|
||||||
|
function getWorkspaceIdAt(index) {
|
||||||
|
return root.getWorkspaceId(root.group, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to update workspaceOccupied
|
||||||
|
function updateWorkspaceOccupied() {
|
||||||
|
root.occupied = Array.from({
|
||||||
|
length: root.shownCount
|
||||||
|
}, (_, i) => {
|
||||||
|
const thisWorkspaceId = getWorkspaceId(root.group, i);
|
||||||
|
return Hyprland.workspaces.values.some(ws => ws.id === thisWorkspaceId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Occupied workspace updates
|
||||||
|
Component.onCompleted: updateWorkspaceOccupied()
|
||||||
|
Connections {
|
||||||
|
target: Hyprland.workspaces
|
||||||
|
function onValuesChanged() {
|
||||||
|
root.updateWorkspaceOccupied();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: Hyprland
|
||||||
|
function onFocusedWorkspaceChanged() {
|
||||||
|
root.updateWorkspaceOccupied();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onGroupChanged: {
|
||||||
|
updateWorkspaceOccupied();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ QuickToggleModel {
|
|||||||
name: Translation.tr("Internet")
|
name: Translation.tr("Internet")
|
||||||
statusText: Network.networkName
|
statusText: Network.networkName
|
||||||
tooltipText: Translation.tr("%1 | Right-click to configure").arg(Network.networkName)
|
tooltipText: Translation.tr("%1 | Right-click to configure").arg(Network.networkName)
|
||||||
icon: Network.materialSymbol
|
icon: Icons.getNetworkMaterialSymbol()
|
||||||
|
|
||||||
toggled: Network.wifiStatus !== "disabled"
|
toggled: Network.wifiStatus !== "disabled"
|
||||||
mainAction: () => Network.toggleWifi()
|
mainAction: () => Network.toggleWifi()
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property real progress: 0
|
||||||
|
default property Item child
|
||||||
|
implicitWidth: child.implicitWidth
|
||||||
|
implicitHeight: child.implicitHeight
|
||||||
|
|
||||||
|
children: [child]
|
||||||
|
|
||||||
|
property var animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this)
|
||||||
|
Behavior on progress {
|
||||||
|
animation: root.animation
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
|
||||||
|
Control {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property list<real> valueWeights: [1]
|
||||||
|
property list<real> values: [0.5]
|
||||||
|
property list<color> valueHighlights: ["white"]
|
||||||
|
property list<color> valueTroughs: []
|
||||||
|
|
||||||
|
readonly property list<real> normalizedValueWeights: {
|
||||||
|
const totalWeight = valueWeights.reduce((sum, weight) => sum + weight, 0)
|
||||||
|
return valueWeights.map(weight => weight / totalWeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property list<real> visualEnds: {
|
||||||
|
let cumsum = 0;
|
||||||
|
let positions = [];
|
||||||
|
for (let i = 0; i < normalizedValueWeights.length; i++) {
|
||||||
|
cumsum += normalizedValueWeights[i];
|
||||||
|
positions.push(cumsum);
|
||||||
|
}
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property list<real> visualPositions: {
|
||||||
|
let positions = [];
|
||||||
|
let lastEnd = 0;
|
||||||
|
for(let i = 0; i < visualEnds.length; i++) {
|
||||||
|
const thisEnd = visualEnds[i];
|
||||||
|
const width = thisEnd - lastEnd;
|
||||||
|
const thisPos = lastEnd + width * values[i];
|
||||||
|
positions.push(thisPos);
|
||||||
|
lastEnd = visualEnds[i];
|
||||||
|
}
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property list<var> visualSegments: {
|
||||||
|
let segs = [];
|
||||||
|
let lastEnd = 0;
|
||||||
|
for(let i = 0; i < visualEnds.length; i++) {
|
||||||
|
const thisEnd = visualEnds[i];
|
||||||
|
const thisPos = visualPositions[i];
|
||||||
|
segs.push([lastEnd, thisPos]);
|
||||||
|
segs.push([thisPos, thisEnd]);
|
||||||
|
lastEnd = visualEnds[i];
|
||||||
|
}
|
||||||
|
return segs;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property list<color> segmentColors: {
|
||||||
|
var cols = [];
|
||||||
|
for(let i = 0; i < valueHighlights.length; i++) {
|
||||||
|
cols.push(valueHighlights[i]);
|
||||||
|
cols.push(valueTroughs[i]);
|
||||||
|
}
|
||||||
|
return cols;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import QtQuick
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
|
||||||
|
Kirigami.Icon {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property real implicitSize: 26
|
||||||
|
implicitWidth: implicitSize
|
||||||
|
implicitHeight: implicitSize
|
||||||
|
|
||||||
|
roundToIconSize: false
|
||||||
|
animated: true // It's just fading from one icon to another
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
StyledRectangle {
|
||||||
|
property bool vertical: false
|
||||||
|
property real startRadius
|
||||||
|
property real endRadius
|
||||||
|
|
||||||
|
topLeftRadius: startRadius
|
||||||
|
topRightRadius: vertical ? startRadius : endRadius
|
||||||
|
bottomLeftRadius: vertical ? endRadius : startRadius
|
||||||
|
bottomRightRadius: endRadius
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
RectangularContainerShape {
|
||||||
|
property bool vertical: false
|
||||||
|
property real startRadius
|
||||||
|
property real endRadius
|
||||||
|
|
||||||
|
topLeftRadius: startRadius
|
||||||
|
topRightRadius: vertical ? startRadius : endRadius
|
||||||
|
bottomLeftRadius: vertical ? endRadius : startRadius
|
||||||
|
bottomRightRadius: endRadius
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
// A type that's both capable of being rows and columns
|
||||||
|
// Qt Row is just a locked down Grid smh
|
||||||
|
// Calling it a Box because that's how row-or-column widget is called in Gtk
|
||||||
|
Grid {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool vertical: false
|
||||||
|
columns: vertical ? 1 : -1
|
||||||
|
rows: vertical ? -1 : 1
|
||||||
|
|
||||||
|
property alias spacing: root.rowSpacing
|
||||||
|
columnSpacing: rowSpacing
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
// Box, Layout version
|
||||||
|
// A type that's both capable of being rows and columns
|
||||||
|
// Qt Row is just a locked down Grid smh
|
||||||
|
// Calling it a Box because that's how row-or-column widget is called in Gtk
|
||||||
|
GridLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool vertical: false
|
||||||
|
columns: vertical ? 1 : -1
|
||||||
|
rows: vertical ? -1 : 1
|
||||||
|
|
||||||
|
property alias spacing: root.rowSpacing
|
||||||
|
columnSpacing: rowSpacing
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
// MouseArea that contains good defaults for buttons
|
||||||
|
MouseArea {
|
||||||
|
id: root
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement for QtQuick Controls DayOfWeek row.
|
||||||
|
* I have to do this because that one is somehow really unreliable in my dynamically loaded widget
|
||||||
|
*/
|
||||||
|
Row {
|
||||||
|
id: root
|
||||||
|
property Component delegate
|
||||||
|
property alias model: repeater.model
|
||||||
|
|
||||||
|
property var locale: Qt.locale()
|
||||||
|
readonly property var firstDayOfWeek: locale.firstDayOfWeek
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: repeater
|
||||||
|
model: Array.from({
|
||||||
|
length: 7
|
||||||
|
}, (_, i) => {
|
||||||
|
const day = (root.firstDayOfWeek + i + 7 - 1) % 7 + 1
|
||||||
|
return ({
|
||||||
|
// Convert Locale day of week enum values to that of Qt enum values for
|
||||||
|
// consistency with DayOfWeekRow. Note that Locale day of week enum values are 0-indexed,
|
||||||
|
// while Qt day of week enum values are 1-indexed.
|
||||||
|
// Refererences:
|
||||||
|
// Locale enum values: https://doc.qt.io/qt-6/qml-qtqml-locale.html#firstDayOfWeek-prop
|
||||||
|
// DayOfWeek model values: https://doc.qt.io/qt-6/qml-qtquick-controls-dayofweekrow.html#delegate-prop
|
||||||
|
// which mentions the enum values in the Qt namespace at: https://doc.qt.io/qt-6/qt.html#DayOfWeek-enum
|
||||||
|
day: day,
|
||||||
|
shortName: root.locale.toString(new Date(2024, 0, day), "ddd")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
delegate: root.delegate
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,10 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
|
||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
import qs.modules.common.functions
|
import qs.modules.common.functions
|
||||||
import qs.modules.waffle.looks
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -36,15 +31,23 @@ Item {
|
|||||||
const diffWeeks = Math.round(diffMillis / root.millisPerWeek);
|
const diffWeeks = Math.round(diffMillis / root.millisPerWeek);
|
||||||
root.targetWeekDiff += diffWeeks;
|
root.targetWeekDiff += diffWeeks;
|
||||||
}
|
}
|
||||||
|
function scrollToToday() {
|
||||||
|
root.targetWeekDiff = 0;
|
||||||
|
}
|
||||||
property int weeksPerScroll: 1
|
property int weeksPerScroll: 1
|
||||||
property real targetWeekDiff: 0
|
property real targetWeekDiff: 0
|
||||||
property real weekDiff: targetWeekDiff
|
property real weekDiff: targetWeekDiff
|
||||||
property int contentWeekDiff: weekDiff // whole part of weekDiff
|
property int contentWeekDiff: weekDiff // whole part of weekDiff
|
||||||
property bool scrolling: false
|
property bool scrolling: false
|
||||||
|
|
||||||
|
property Animation scrollAnimation: NumberAnimation {
|
||||||
|
duration: Config.options.calendar.animate ? Appearance.animation.scroll.duration : 0
|
||||||
|
easing.type: Appearance.animation.scroll.type
|
||||||
|
easing.bezierCurve: Appearance.animation.scroll.bezierCurve
|
||||||
|
}
|
||||||
Behavior on weekDiff {
|
Behavior on weekDiff {
|
||||||
id: weekScrollBehavior
|
id: weekScrollBehavior
|
||||||
animation: Looks.transition.scroll.createObject(this)
|
animation: root.scrollAnimation
|
||||||
}
|
}
|
||||||
Timer {
|
Timer {
|
||||||
id: scrollAnimationCheckTimer
|
id: scrollAnimationCheckTimer
|
||||||
@@ -56,12 +59,20 @@ Item {
|
|||||||
scrollAnimationCheckTimer.restart();
|
scrollAnimationCheckTimer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
property var wheelAction: (wheel) => {
|
||||||
anchors.fill: parent
|
// Reverse cuz scrolling down should advance
|
||||||
onWheel: wheel => {
|
const sign = wheel.angleDelta.y / 120 * -1;
|
||||||
root.targetWeekDiff += wheel.angleDelta.y / 120 * -root.weeksPerScroll; // Reverse cuz scrolling down should advance
|
if (Config.options.calendar.weekScrollPrecision) {
|
||||||
|
root.targetWeekDiff += sign * root.weeksPerScroll;
|
||||||
|
} else {
|
||||||
|
scrollMonthsAndSnap(sign);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
onWheel: (wheel) => root.wheelAction(wheel)
|
||||||
|
}
|
||||||
|
|
||||||
// Date calculations
|
// Date calculations
|
||||||
readonly property int millisPerWeek: 7 * 24 * 60 * 60 * 1000
|
readonly property int millisPerWeek: 7 * 24 * 60 * 60 * 1000
|
||||||
@@ -82,14 +93,16 @@ Item {
|
|||||||
return DateUtils.getIthDayDateOfSameWeek(dateInTargetWeek, root.focusDayOfWeekIndex - root.locale.firstDayOfWeek, root.locale.firstdayOfWeek); // 4 = Thursday
|
return DateUtils.getIthDayDateOfSameWeek(dateInTargetWeek, root.focusDayOfWeekIndex - root.locale.firstDayOfWeek, root.locale.firstdayOfWeek); // 4 = Thursday
|
||||||
}
|
}
|
||||||
property int focusedMonth: focusedDate.getMonth() + 1 // 0-indexed -> 1-indexed
|
property int focusedMonth: focusedDate.getMonth() + 1 // 0-indexed -> 1-indexed
|
||||||
|
property string title: locale.toString(focusedDate, "MMMM yyyy")
|
||||||
|
|
||||||
// Sizes
|
// Sizes
|
||||||
property real verticalPadding: 0
|
property real verticalPadding: 0
|
||||||
|
property real horizontalPadding: 0
|
||||||
property real buttonSize: 40
|
property real buttonSize: 40
|
||||||
property real buttonSpacing: 2
|
property real buttonSpacing: 2
|
||||||
property real buttonVerticalSpacing: buttonSpacing
|
property real buttonVerticalSpacing: buttonSpacing
|
||||||
implicitHeight: (6 * buttonSize) + (5 * buttonVerticalSpacing) + (2 * verticalPadding)
|
implicitHeight: (6 * buttonSize) + (5 * buttonVerticalSpacing) + (2 * verticalPadding)
|
||||||
implicitWidth: weeksColumn.implicitWidth
|
implicitWidth: weeksColumn.implicitWidth + (2 * horizontalPadding)
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@@ -97,6 +110,8 @@ Item {
|
|||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
right: parent.right
|
right: parent.right
|
||||||
|
leftMargin: root.horizontalPadding
|
||||||
|
rightMargin: root.horizontalPadding
|
||||||
}
|
}
|
||||||
y: {
|
y: {
|
||||||
const spacePerExtraRow = root.buttonSize + root.buttonVerticalSpacing;
|
const spacePerExtraRow = root.buttonSize + root.buttonVerticalSpacing;
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
columns: 1
|
||||||
|
property real totalDuration: 250
|
||||||
|
property real interval: totalDuration / count
|
||||||
|
|
||||||
|
property list<QtObject> choreographableChildren: children.filter(c => {
|
||||||
|
return c.hasOwnProperty("progress")
|
||||||
|
})
|
||||||
|
readonly property int count: choreographableChildren.length
|
||||||
|
|
||||||
|
property bool shown: false
|
||||||
|
onShownChanged: {
|
||||||
|
// When hiding, hide all at once
|
||||||
|
if (!shown) {
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
choreographableChildren[i].progress = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// When showing, choreograph
|
||||||
|
root.choreographIndex = 0;
|
||||||
|
}
|
||||||
|
property int choreographIndex: count
|
||||||
|
Timer {
|
||||||
|
id: choreographTimer
|
||||||
|
interval: root.interval
|
||||||
|
property bool step: root.shown && root.choreographIndex < root.count
|
||||||
|
running: step
|
||||||
|
repeat: step
|
||||||
|
onTriggered: {
|
||||||
|
const index = root.choreographIndex;
|
||||||
|
root.choreographableChildren[index].progress = 1;
|
||||||
|
root.choreographIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
FadeLoader {
|
||||||
|
id: root
|
||||||
|
onActiveChanged: if (active) item.shown = true
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
property double diameter
|
property real diameter
|
||||||
|
|
||||||
implicitWidth: diameter
|
implicitWidth: diameter
|
||||||
implicitHeight: diameter
|
implicitHeight: diameter
|
||||||
|
|||||||
@@ -42,8 +42,7 @@ Item {
|
|||||||
active: root.fill
|
active: root.fill
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
sourceComponent: Rectangle {
|
sourceComponent: Circle {
|
||||||
radius: 9999
|
|
||||||
color: root.colSecondary
|
color: root.colSecondary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import qs.modules.common.functions
|
|||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Qt5Compat.GraphicalEffects
|
import QtQuick.Effects
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A progress bar with both ends rounded and text acts as clipping like OneUI 7's battery indicator.
|
* A progress bar with both ends rounded and text acts as clipping like OneUI 7's battery indicator.
|
||||||
@@ -16,15 +16,17 @@ ProgressBar {
|
|||||||
property color highlightColor: Appearance?.colors.colOnSecondaryContainer ?? "#685496"
|
property color highlightColor: Appearance?.colors.colOnSecondaryContainer ?? "#685496"
|
||||||
property color trackColor: ColorUtils.transparentize(highlightColor, 0.5) ?? "#F1D3F9"
|
property color trackColor: ColorUtils.transparentize(highlightColor, 0.5) ?? "#F1D3F9"
|
||||||
property alias radius: contentItem.radius
|
property alias radius: contentItem.radius
|
||||||
|
property alias progressRadius: progressFill.radius
|
||||||
property string text
|
property string text
|
||||||
default property Item textMask: Item {
|
default property Item textMask: Item {
|
||||||
width: valueBarWidth
|
width: root.valueBarWidth
|
||||||
height: valueBarHeight
|
height: root.valueBarHeight
|
||||||
StyledText {
|
VisuallyCenteredStyledText {
|
||||||
anchors.centerIn: parent
|
anchors.fill: parent
|
||||||
font: root.font
|
font: root.font
|
||||||
text: root.text
|
text: root.text
|
||||||
}
|
}
|
||||||
|
layer.enabled: true
|
||||||
}
|
}
|
||||||
|
|
||||||
text: Math.round(value * 100)
|
text: Math.round(value * 100)
|
||||||
@@ -38,10 +40,9 @@ ProgressBar {
|
|||||||
implicitWidth: valueBarWidth
|
implicitWidth: valueBarWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Rectangle {
|
contentItem: Pill {
|
||||||
id: contentItem
|
id: contentItem
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: 9999
|
|
||||||
color: root.trackColor
|
color: root.trackColor
|
||||||
visible: false
|
visible: false
|
||||||
|
|
||||||
@@ -80,22 +81,40 @@ ProgressBar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OpacityMask {
|
Rectangle {
|
||||||
id: roundingMask
|
id: contentMaskRect
|
||||||
|
anchors.fill: contentItem
|
||||||
|
width: contentItem.width
|
||||||
|
height: contentItem.height
|
||||||
|
radius: contentItem.radius
|
||||||
|
layer.enabled: true
|
||||||
visible: false
|
visible: false
|
||||||
anchors.fill: parent
|
|
||||||
source: contentItem
|
|
||||||
maskSource: Rectangle {
|
|
||||||
width: contentItem.width
|
|
||||||
height: contentItem.height
|
|
||||||
radius: contentItem.radius
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OpacityMask {
|
Item {
|
||||||
anchors.fill: parent
|
// textMask has to be rendered somewhere so we put it in a practically invisible item
|
||||||
source: roundingMask
|
anchors.centerIn: parent
|
||||||
invert: true
|
opacity: 0
|
||||||
maskSource: root.textMask
|
Component.onCompleted: root.textMask.layer.enabled = true // for multieffect masking
|
||||||
|
children: [root.textMask]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaskMultiEffect {
|
||||||
|
id: boxClip
|
||||||
|
anchors.fill: parent
|
||||||
|
source: contentItem
|
||||||
|
maskSource: contentMaskRect
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
MaskMultiEffect {
|
||||||
|
id: textClip
|
||||||
|
anchors.fill: parent
|
||||||
|
implicitWidth: contentItem.implicitWidth
|
||||||
|
implicitHeight: contentItem.implicitHeight
|
||||||
|
source: boxClip
|
||||||
|
maskSource: root.textMask
|
||||||
|
maskInverted: true
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import qs.modules.common
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
property color sourceColor: "black"
|
||||||
|
|
||||||
|
colorization: 1
|
||||||
|
brightness: 1 - sourceColor.hslLightness
|
||||||
|
|
||||||
|
Behavior on colorizationColor {
|
||||||
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Shapes
|
||||||
|
import qs.modules.common
|
||||||
|
|
||||||
|
AbstractCombinedProgressBar {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int implicitSize: 30
|
||||||
|
property int lineWidth: 2
|
||||||
|
property real gapAngle: 360 / 18
|
||||||
|
|
||||||
|
valueHighlights: [Appearance.colors.colPrimary, Appearance.colors.colTertiary]
|
||||||
|
valueTroughs: [Appearance.colors.colSecondaryContainer, Appearance.colors.colTertiaryContainer]
|
||||||
|
|
||||||
|
property bool enableAnimation: true
|
||||||
|
property int animationDuration: 800
|
||||||
|
property var easingType: Easing.OutCubic
|
||||||
|
|
||||||
|
implicitWidth: implicitSize
|
||||||
|
implicitHeight: implicitSize
|
||||||
|
|
||||||
|
readonly property real centerX: root.width / 2
|
||||||
|
readonly property real centerY: root.height / 2
|
||||||
|
readonly property real arcRadius: root.implicitSize / 2 - root.lineWidth
|
||||||
|
readonly property real startAngle: -90
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
implicitWidth: root.implicitSize
|
||||||
|
implicitHeight: root.implicitSize
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNegligibleSegment(seg: var): bool {
|
||||||
|
const range = seg[1] - seg[0];
|
||||||
|
return range < 1 / 360; // TODO make this less arbitrary
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.visualSegments
|
||||||
|
delegate: Shape {
|
||||||
|
id: segShape
|
||||||
|
required property int index
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
property bool negligible: root.isNegligibleSegment(modelData)
|
||||||
|
property bool atStart: index == 0
|
||||||
|
property bool atEnd: index == root.visualSegments.length - 1
|
||||||
|
property real displaySegStart: {
|
||||||
|
var i = index;
|
||||||
|
while ((i > 0 && root.isNegligibleSegment(root.visualSegments[i - 1])))
|
||||||
|
i--;
|
||||||
|
return root.visualSegments[i][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
layer.enabled: true
|
||||||
|
layer.smooth: true
|
||||||
|
preferredRendererType: Shape.CurveRenderer
|
||||||
|
ShapePath {
|
||||||
|
strokeColor: segShape.negligible ? "transparent" : root.segmentColors[segShape.index % root.segmentColors.length]
|
||||||
|
strokeWidth: segShape.negligible ? 0 : root.lineWidth
|
||||||
|
capStyle: ShapePath.RoundCap
|
||||||
|
fillColor: "transparent"
|
||||||
|
PathAngleArc {
|
||||||
|
centerX: root.centerX
|
||||||
|
centerY: root.centerY
|
||||||
|
radiusX: root.arcRadius
|
||||||
|
radiusY: root.arcRadius
|
||||||
|
startAngle: root.startAngle + 360 * segShape.displaySegStart + root.gapAngle / 2
|
||||||
|
sweepAngle: 360 * (segShape.modelData[1] - segShape.displaySegStart) - root.gapAngle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,10 @@ Flow {
|
|||||||
]
|
]
|
||||||
property var currentValue: null
|
property var currentValue: null
|
||||||
|
|
||||||
|
function focusSelectedChild() {
|
||||||
|
children.find(c => c.value == currentValue).forceActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
signal selected(var newValue)
|
signal selected(var newValue)
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
@@ -31,6 +35,7 @@ Flow {
|
|||||||
id: paletteButton
|
id: paletteButton
|
||||||
required property var modelData
|
required property var modelData
|
||||||
required property int index
|
required property int index
|
||||||
|
readonly property var value: modelData.value
|
||||||
onYChanged: {
|
onYChanged: {
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
paletteButton.leftmost = true
|
paletteButton.leftmost = true
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ RippleButton {
|
|||||||
}
|
}
|
||||||
StyledSwitch {
|
StyledSwitch {
|
||||||
id: switchWidget
|
id: switchWidget
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
down: root.down
|
down: root.down
|
||||||
Layout.fillWidth: false
|
Layout.fillWidth: false
|
||||||
checked: root.checked
|
checked: root.checked
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.modules.common
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias load: loader.activeAsync
|
||||||
|
property bool shown: true // By default show immediately when loaded
|
||||||
|
property alias component: loader.component
|
||||||
|
|
||||||
|
property alias fade: opacityBehavior.enabled
|
||||||
|
property alias animation: opacityBehavior.animation
|
||||||
|
|
||||||
|
opacity: loader.active && shown ? 1 : 0
|
||||||
|
visible: opacity > 0
|
||||||
|
Behavior on opacity {
|
||||||
|
id: opacityBehavior
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: loader
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int fallbackIndex: 0
|
||||||
|
property list<url> fallbacks: []
|
||||||
|
property list<Component> fallbackComponents: []
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === Loader.Error && fallbackIndex < fallbacks.length) {
|
||||||
|
if (fallbacks[fallbackIndex]) {
|
||||||
|
source = fallbacks[fallbackIndex];
|
||||||
|
if (fallbackComponents[fallbackIndex]) {
|
||||||
|
console.warn("[FallbackLoader] Both fallbacks urls and components are set, using url fallback");
|
||||||
|
}
|
||||||
|
} else if (fallbackComponents[fallbackIndex]) {
|
||||||
|
sourceComponent = fallbackComponents[fallbackIndex];
|
||||||
|
} else {
|
||||||
|
console.error("[FallbackLoader] Out of fallbacks, tried all", fallbackIndex);
|
||||||
|
}
|
||||||
|
fallbackIndex += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias longestText: longestTextMetrics.text
|
||||||
|
property alias font: longestTextMetrics.font
|
||||||
|
|
||||||
|
implicitWidth: longestTextMetrics.width
|
||||||
|
implicitHeight: longestTextMetrics.height
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: longestTextMetrics
|
||||||
|
text: root.longestText
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
AbstractChoreographable {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
progress: 0
|
||||||
|
property bool vertical: true
|
||||||
|
property bool reverseDirection: false
|
||||||
|
property real distance: 15
|
||||||
|
|
||||||
|
readonly property real directionMultiplier: reverseDirection ? -1 : 1
|
||||||
|
|
||||||
|
Component.onCompleted: syncProgress()
|
||||||
|
onProgressChanged: syncProgress()
|
||||||
|
|
||||||
|
function syncProgress() {
|
||||||
|
const progressDistance = distance * (1 - progress) * directionMultiplier;
|
||||||
|
root.child.opacity = progress
|
||||||
|
if (vertical) {
|
||||||
|
root.child.y = progressDistance
|
||||||
|
} else {
|
||||||
|
root.child.x = progressDistance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,6 +42,7 @@ Button {
|
|||||||
property color colBackgroundToggled: Appearance?.colors.colPrimary ?? "#65558F"
|
property color colBackgroundToggled: Appearance?.colors.colPrimary ?? "#65558F"
|
||||||
property color colBackgroundToggledHover: Appearance?.colors.colPrimaryHover ?? "#77699C"
|
property color colBackgroundToggledHover: Appearance?.colors.colPrimaryHover ?? "#77699C"
|
||||||
property color colBackgroundToggledActive: Appearance?.colors.colPrimaryActive ?? "#D6CEE2"
|
property color colBackgroundToggledActive: Appearance?.colors.colPrimaryActive ?? "#D6CEE2"
|
||||||
|
property color colFocusRing: Appearance.colors.colOnSecondaryContainer
|
||||||
|
|
||||||
property real radius: root.down ? root.buttonRadiusPressed : root.buttonRadius
|
property real radius: root.down ? root.buttonRadiusPressed : root.buttonRadius
|
||||||
property real leftRadius: root.down ? root.buttonRadiusPressed : root.buttonRadius
|
property real leftRadius: root.down ? root.buttonRadiusPressed : root.buttonRadius
|
||||||
@@ -117,7 +118,6 @@ Button {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
property bool tabbedTo: root.focus && (focusReason === Qt.TabFocusReason || focusReason === Qt.BacktabFocusReason)
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: buttonBackground
|
id: buttonBackground
|
||||||
topLeftRadius: root.leftRadius
|
topLeftRadius: root.leftRadius
|
||||||
@@ -130,9 +130,25 @@ Button {
|
|||||||
Behavior on color {
|
Behavior on color {
|
||||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
border.width: root.tabbedTo ? 2 : 0
|
z: visualFocus ? 1 : 0
|
||||||
border.color: Appearance.colors.colSecondary
|
Rectangle {
|
||||||
|
id: focusRing
|
||||||
|
topLeftRadius: root.leftRadius - anchors.margins
|
||||||
|
topRightRadius: root.rightRadius - anchors.margins
|
||||||
|
bottomLeftRadius: root.leftRadius - anchors.margins
|
||||||
|
bottomRightRadius: root.rightRadius - anchors.margins
|
||||||
|
visible: root.visualFocus
|
||||||
|
color: "transparent"
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: -4
|
||||||
|
}
|
||||||
|
border {
|
||||||
|
color: root.colFocusRing
|
||||||
|
width: 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: StyledText {
|
contentItem: StyledText {
|
||||||
|
|||||||
@@ -20,11 +20,12 @@ StyledText {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property Animation fillAnimation: NumberAnimation {
|
||||||
|
duration: Appearance?.animation.elementMoveFast.duration ?? 200
|
||||||
|
easing.type: Appearance?.animation.elementMoveFast.type ?? Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance?.animation.elementMoveFast.bezierCurve ?? [0.34, 0.80, 0.34, 1.00, 1, 1]
|
||||||
|
}
|
||||||
Behavior on fill { // Leaky leaky, no good
|
Behavior on fill { // Leaky leaky, no good
|
||||||
NumberAnimation {
|
animation: root.fillAnimation
|
||||||
duration: Appearance?.animation.elementMoveFast.duration ?? 200
|
|
||||||
easing.type: Appearance?.animation.elementMoveFast.type ?? Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Appearance?.animation.elementMoveFast.bezierCurve ?? [0.34, 0.80, 0.34, 1.00, 1, 1]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ MouseArea { // Notification group area
|
|||||||
automaticallyReset: false
|
automaticallyReset: false
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||||
|
|
||||||
onPressed: {
|
onPressed: (mouse) => {
|
||||||
if (mouse.button === Qt.RightButton)
|
if (mouse.button === Qt.RightButton)
|
||||||
root.toggleExpanded();
|
root.toggleExpanded();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
radius: Math.min(width, height) / 2
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.modules.common.models as M
|
||||||
|
import "shapes/material-shapes.js" as MaterialShapes
|
||||||
|
import "shapes/shapes/corner-rounding.js" as CornerRounding
|
||||||
|
import "shapes/geometry/offset.js" as Offset
|
||||||
|
|
||||||
|
// For returning the points
|
||||||
|
M.NestableObject {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property real width
|
||||||
|
required property real height
|
||||||
|
property real radius: 0
|
||||||
|
property real topLeftRadius: radius
|
||||||
|
property real topRightRadius: radius
|
||||||
|
property real bottomLeftRadius: radius
|
||||||
|
property real bottomRightRadius: radius
|
||||||
|
property real xOffset: 0
|
||||||
|
property real yOffset: 0
|
||||||
|
|
||||||
|
readonly property real radiusLimit: Math.min(width, height) / 2
|
||||||
|
readonly property real effectiveTopLeftRadius: Math.min(topLeftRadius, radiusLimit)
|
||||||
|
readonly property real effectiveTopRightRadius: Math.min(topRightRadius, radiusLimit)
|
||||||
|
readonly property real effectiveBottomLeftRadius: Math.min(bottomLeftRadius, radiusLimit)
|
||||||
|
readonly property real effectiveBottomRightRadius: Math.min(bottomRightRadius, radiusLimit)
|
||||||
|
|
||||||
|
// Clockwise starting from bottom
|
||||||
|
property list<var> bottomPoints: [
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width - effectiveBottomRightRadius, yOffset + height), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width / 2, yOffset + height), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xOffset + effectiveBottomLeftRadius, yOffset + height), new CornerRounding.CornerRounding(0)),
|
||||||
|
]
|
||||||
|
property list<var> leftPoints: [
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xOffset + 0, yOffset + height - effectiveBottomLeftRadius), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xOffset + 0, yOffset + height / 2), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xOffset + 0, yOffset + effectiveTopLeftRadius), new CornerRounding.CornerRounding(0)),
|
||||||
|
]
|
||||||
|
property list<var> topPoints: [
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xOffset + effectiveTopLeftRadius, yOffset + 0), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width / 2, yOffset + 0), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width - effectiveTopRightRadius, yOffset + 0), new CornerRounding.CornerRounding(0)),
|
||||||
|
]
|
||||||
|
property list<var> rightPoints: [
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width, yOffset + effectiveTopRightRadius), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width, yOffset + height / 2), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xOffset + width, yOffset + height - effectiveBottomRightRadius), new CornerRounding.CornerRounding(0)),
|
||||||
|
]
|
||||||
|
|
||||||
|
function getFirstBottomPoints() {
|
||||||
|
return bottomPoints.slice(Math.floor(bottomPoints.length / 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLastBottomPoints() {
|
||||||
|
return bottomPoints.slice(0, Math.floor(bottomPoints.length / 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBottomLeftPoint(extraXOffset = 0, extraYOffset = 0, radius = undefined) {
|
||||||
|
if (radius === undefined) radius = effectiveBottomLeftRadius;
|
||||||
|
return new MaterialShapes.PointNRound(new Offset.Offset(xOffset + extraXOffset + 0, yOffset + extraYOffset + height), new CornerRounding.CornerRounding(radius))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTopLeftPoint(extraXOffset = 0, extraYOffset = 0, radius = undefined) {
|
||||||
|
if (radius === undefined) radius = effectiveTopLeftRadius;
|
||||||
|
return new MaterialShapes.PointNRound(new Offset.Offset(xOffset + extraXOffset + 0, yOffset + extraYOffset + 0), new CornerRounding.CornerRounding(radius))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTopRightPoint(extraXOffset = 0, extraYOffset = 0, radius = undefined) {
|
||||||
|
if (radius === undefined) radius = effectiveTopRightRadius;
|
||||||
|
return new MaterialShapes.PointNRound(new Offset.Offset(xOffset + extraXOffset + width, yOffset + extraYOffset + 0), new CornerRounding.CornerRounding(radius))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBottomRightPoint(extraXOffset = 0, extraYOffset = 0, radius = undefined) {
|
||||||
|
if (radius === undefined) radius = effectiveBottomRightRadius;
|
||||||
|
return new MaterialShapes.PointNRound(new Offset.Offset(xOffset + extraXOffset + width, yOffset + extraYOffset + height), new CornerRounding.CornerRounding(radius))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFullShape() {
|
||||||
|
const points = [
|
||||||
|
...getFirstBottomPoints(),
|
||||||
|
getBottomLeftPoint(),
|
||||||
|
...leftPoints,
|
||||||
|
getTopLeftPoint(),
|
||||||
|
...topPoints,
|
||||||
|
getTopRightPoint(),
|
||||||
|
...rightPoints,
|
||||||
|
getBottomRightPoint(),
|
||||||
|
...getLastBottomPoints(),
|
||||||
|
]
|
||||||
|
return MaterialShapes.customPolygon(points);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ Button {
|
|||||||
property color colBackgroundToggledHover: Appearance?.colors.colPrimaryHover ?? "#77699C"
|
property color colBackgroundToggledHover: Appearance?.colors.colPrimaryHover ?? "#77699C"
|
||||||
property color colRipple: Appearance?.colors.colLayer1Active ?? "#D6CEE2"
|
property color colRipple: Appearance?.colors.colLayer1Active ?? "#D6CEE2"
|
||||||
property color colRippleToggled: Appearance?.colors.colPrimaryActive ?? "#D6CEE2"
|
property color colRippleToggled: Appearance?.colors.colPrimaryActive ?? "#D6CEE2"
|
||||||
|
property color colFocusRing: Appearance.colors.colOnSecondaryContainer
|
||||||
|
|
||||||
opacity: root.enabled ? 1 : 0.4
|
opacity: root.enabled ? 1 : 0.4
|
||||||
property color buttonColor: ColorUtils.transparentize(root.toggled ?
|
property color buttonColor: ColorUtils.transparentize(root.toggled ?
|
||||||
@@ -180,6 +181,22 @@ Button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
z: visualFocus ? 1 : 0
|
||||||
|
Rectangle {
|
||||||
|
id: focusRing
|
||||||
|
radius: buttonBackground.radius - anchors.margins
|
||||||
|
visible: root.visualFocus
|
||||||
|
color: "transparent"
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: -4
|
||||||
|
}
|
||||||
|
border {
|
||||||
|
color: root.colFocusRing
|
||||||
|
width: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contentItem: StyledText {
|
contentItem: StyledText {
|
||||||
text: root.buttonText
|
text: root.buttonText
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// https://m3.material.io/foundations/interaction/states/state-layers
|
||||||
|
enum State {
|
||||||
|
Hover, Focus, Press, Drag
|
||||||
|
}
|
||||||
|
|
||||||
|
property var state: StateLayer.State.Hover
|
||||||
|
opacity: switch(state) {
|
||||||
|
case StateLayer.State.Hover: return 0.08;
|
||||||
|
case StateLayer.State.Focus: return 0.1;
|
||||||
|
case StateLayer.State.Press: return 0.1;
|
||||||
|
case StateLayer.State.Drag: return 0.16;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import qs.modules.common as C
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool hover: false
|
||||||
|
property bool press: false
|
||||||
|
property bool drag: false
|
||||||
|
property color contentColor: C.Appearance.m3colors.m3onBackground
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
FadeLoader {
|
||||||
|
id: hoverLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
shown: root.hover
|
||||||
|
sourceComponent: StateLayer {
|
||||||
|
state: StateLayer.State.Hover
|
||||||
|
color: root.contentColor
|
||||||
|
topLeftRadius: root.topLeftRadius
|
||||||
|
topRightRadius: root.topRightRadius
|
||||||
|
bottomLeftRadius: root.bottomLeftRadius
|
||||||
|
bottomRightRadius: root.bottomRightRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FadeLoader {
|
||||||
|
id: focusLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
shown: root.focus
|
||||||
|
sourceComponent: StateLayer {
|
||||||
|
state: StateLayer.State.Focus
|
||||||
|
color: root.contentColor
|
||||||
|
topLeftRadius: root.topLeftRadius
|
||||||
|
topRightRadius: root.topRightRadius
|
||||||
|
bottomLeftRadius: root.bottomLeftRadius
|
||||||
|
bottomRightRadius: root.bottomRightRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FadeLoader {
|
||||||
|
id: pressLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
shown: root.press
|
||||||
|
sourceComponent: StateLayer {
|
||||||
|
state: StateLayer.State.Press
|
||||||
|
color: root.contentColor
|
||||||
|
topLeftRadius: root.topLeftRadius
|
||||||
|
topRightRadius: root.topRightRadius
|
||||||
|
bottomLeftRadius: root.bottomLeftRadius
|
||||||
|
bottomRightRadius: root.bottomRightRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FadeLoader {
|
||||||
|
id: dragLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
shown: root.drag
|
||||||
|
sourceComponent: StateLayer {
|
||||||
|
state: StateLayer.State.Drag
|
||||||
|
color: root.contentColor
|
||||||
|
topLeftRadius: root.topLeftRadius
|
||||||
|
topRightRadius: root.topRightRadius
|
||||||
|
bottomLeftRadius: root.bottomLeftRadius
|
||||||
|
bottomRightRadius: root.bottomRightRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import ".."
|
||||||
|
import "../functions"
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias radius: bg.radius
|
||||||
|
property alias contentLayer: bg.contentLayer
|
||||||
|
|
||||||
|
property color colFocusRing: Appearance.colors.colOnSecondaryContainer
|
||||||
|
property color colBackground: checked ? colBackgroundChecked : colBackgroundUnchecked
|
||||||
|
property color colForeground: checked ? colForegroundChecked : colForegroundUnchecked
|
||||||
|
|
||||||
|
property color colBackgroundUnchecked: ColorUtils.transparentize(Appearance.colors.colLayer4Base, 1)
|
||||||
|
property color colBackgroundChecked: Appearance.colors.colPrimary
|
||||||
|
property color colForegroundUnchecked: Appearance.colors.colOnLayer4
|
||||||
|
property color colForegroundChecked: Appearance.colors.colOnPrimary
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
opacity: root.enabled ? 1 : 0.5
|
||||||
|
|
||||||
|
Behavior on colBackground {
|
||||||
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
Behavior on colForeground {
|
||||||
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
background: StyledRectangle {
|
||||||
|
id: bg
|
||||||
|
implicitHeight: root.contentItem.implicitHeight
|
||||||
|
implicitWidth: root.contentItem.implicitWidth
|
||||||
|
|
||||||
|
radius: Math.min(width, height) / 2
|
||||||
|
color: root.colBackground
|
||||||
|
|
||||||
|
StateOverlay {
|
||||||
|
anchors.fill: parent
|
||||||
|
hover: root.hovered && root.enabled
|
||||||
|
press: root.pressed && root.enabled
|
||||||
|
focus: false // We use a ring instead
|
||||||
|
radius: bg.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: focusRing
|
||||||
|
radius: bg.radius - anchors.margins
|
||||||
|
visible: root.visualFocus
|
||||||
|
color: "transparent"
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: -4
|
||||||
|
}
|
||||||
|
border {
|
||||||
|
color: root.colFocusRing
|
||||||
|
width: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
z: visualFocus ? 1 : 0
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
implicitWidth: buttonText.implicitWidth
|
||||||
|
implicitHeight: buttonText.implicitHeight
|
||||||
|
VisuallyCenteredStyledText {
|
||||||
|
id: buttonText
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: root.text
|
||||||
|
color: root.colForeground
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import qs.modules.common
|
||||||
|
|
||||||
|
AbstractCombinedProgressBar {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property real valueBarWidth: 120
|
||||||
|
property real valueBarHeight: 4
|
||||||
|
property real valueBarGap: 4
|
||||||
|
property real valueBarInnerRadius: Appearance.rounding.unsharpen
|
||||||
|
valueHighlights: [Appearance.colors.colPrimary, Appearance.colors.colTertiary]
|
||||||
|
valueTroughs: [Appearance.colors.colSecondaryContainer, Appearance.colors.colTertiaryContainer]
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
implicitWidth: root.valueBarWidth
|
||||||
|
implicitHeight: root.valueBarHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
// "negligible" = too small that it'd look weird when shown
|
||||||
|
function isNegligibleSegment(seg: var): bool {
|
||||||
|
const wdth = seg[1] - seg[0];
|
||||||
|
const visualWidth = availableWidth * wdth;
|
||||||
|
return (visualWidth <= valueBarGap + valueBarHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
Repeater {
|
||||||
|
model: root.visualSegments
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property int index
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
visible: !root.isNegligibleSegment(modelData)
|
||||||
|
property bool atStart: index == 0
|
||||||
|
property bool atEnd: index == root.visualSegments.length - 1
|
||||||
|
property real displaySegStart: { // swallow previous segments if they're "negligible"
|
||||||
|
var i = index;
|
||||||
|
while ((i > 0 && root.isNegligibleSegment(root.visualSegments[i-1])))
|
||||||
|
i--;
|
||||||
|
return root.visualSegments[i][0]
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
x: {
|
||||||
|
var result = root.availableWidth * displaySegStart;
|
||||||
|
if (!atStart) result += root.valueBarGap / 2;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
width: {
|
||||||
|
var result = root.availableWidth * (modelData[1] - displaySegStart)
|
||||||
|
if (atStart || atEnd) result -= root.valueBarGap / 2;
|
||||||
|
else result -= root.valueBarGap;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
color: root.segmentColors[index % root.segmentColors.length]
|
||||||
|
|
||||||
|
property real startRadius: atStart ? height / 2 : root.valueBarInnerRadius
|
||||||
|
property real endRadius: atEnd ? height / 2 : root.valueBarInnerRadius
|
||||||
|
|
||||||
|
topLeftRadius: startRadius
|
||||||
|
bottomLeftRadius: startRadius
|
||||||
|
topRightRadius: endRadius
|
||||||
|
bottomRightRadius: endRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
StyledButton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias implicitSize: root.implicitHeight
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
property alias iconSize: icon.iconSize
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
MaterialSymbol {
|
||||||
|
id: icon
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: root.colForeground
|
||||||
|
text: root.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.modules.common as C
|
||||||
|
|
||||||
|
// This is to enable future fancy styles for rectangles. Some ideas:
|
||||||
|
// - normal rounded rect
|
||||||
|
// - osk.sh
|
||||||
|
// - 3d
|
||||||
|
// i hope i actually get to this and not shrimply forget
|
||||||
|
// aaaaa i realized for this to work i would have to make this for shapes in general not just rects
|
||||||
|
Rectangle {
|
||||||
|
enum ContentLayer { Background, Pane, Group, Subgroup, Control }
|
||||||
|
property var contentLayer: StyledRectangle.ContentLayer.Pane // To appropriately add effects like shadows/3d-ization
|
||||||
|
|
||||||
|
color: switch(contentLayer) {
|
||||||
|
case StyledRectangle.ContentLayer.Background: C.Appearance.colors.colLayer0;
|
||||||
|
case StyledRectangle.ContentLayer.Pane: C.Appearance.colors.colLayer1;
|
||||||
|
case StyledRectangle.ContentLayer.Group: C.Appearance.colors.colLayer2;
|
||||||
|
case StyledRectangle.ContentLayer.Subgroup: C.Appearance.colors.colLayer3;
|
||||||
|
case StyledRectangle.ContentLayer.Control: C.Appearance.colors.colLayer4;
|
||||||
|
default: C.Appearance.colors.colLayer1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ Text {
|
|||||||
|
|
||||||
component Anim: NumberAnimation {
|
component Anim: NumberAnimation {
|
||||||
target: root
|
target: root
|
||||||
duration: 300 / 2
|
duration: 130
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,8 @@ Text {
|
|||||||
Anim {
|
Anim {
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
to: 0
|
to: 0
|
||||||
easing.type: Easing.InSine
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PropertyAction {} // Tie the text update to this point (we don't want it to happen during the first slide+fade)
|
PropertyAction {} // Tie the text update to this point (we don't want it to happen during the first slide+fade)
|
||||||
@@ -83,7 +84,8 @@ Text {
|
|||||||
Anim {
|
Anim {
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
to: 1
|
to: 1
|
||||||
easing.type: Easing.OutSine
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.modules.common as C
|
||||||
|
|
||||||
|
FallbackLoader {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string componentName
|
||||||
|
property string context // Path for the builtin component
|
||||||
|
|
||||||
|
readonly property string componentNameWithExt: componentName.endsWith(".qml") ? componentName : `${componentName}.qml`
|
||||||
|
|
||||||
|
source: `${C.Directories.userComponents}/${componentNameWithExt}`
|
||||||
|
fallbacks: [
|
||||||
|
...(context ? [ `${context}/${componentNameWithExt}` ] : []),
|
||||||
|
componentNameWithExt
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias textWidget: textWidget
|
||||||
|
property alias text: textWidget.text
|
||||||
|
property alias horizontalAlignment: textWidget.horizontalAlignment
|
||||||
|
property alias verticalAlignment: textWidget.verticalAlignment
|
||||||
|
property alias font: textWidget.font
|
||||||
|
property alias color: textWidget.color
|
||||||
|
property alias elide: textWidget.elide
|
||||||
|
property alias wrapMode: textWidget.wrapMode
|
||||||
|
property alias animateChange: textWidget.animateChange
|
||||||
|
|
||||||
|
// In many cases the baseline is a bit high to accomodate the dangling parts of "g" and "y",
|
||||||
|
// making most text (especiall number-only text) not well-balanced.
|
||||||
|
// This adjusts the rounding to make sure the text gets lowered if not internally pixel-aligned
|
||||||
|
property bool lowerBias: true
|
||||||
|
|
||||||
|
implicitWidth: textMetrics.width
|
||||||
|
implicitHeight: textMetrics.height
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: textMetrics
|
||||||
|
font: root.font
|
||||||
|
text: root.text
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: textWidget
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
y: {
|
||||||
|
const value = (parent.height - textMetrics.height) / 2;
|
||||||
|
return root.lowerBias ? Math.ceil(value) : Math.round(value);
|
||||||
|
}
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.services as S
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract morphed panel to be used in TopLayerPanel.
|
||||||
|
* Screen width and height are to be supplied when declared in the top layer panel
|
||||||
|
* Others are to be declared by panels deriving from this
|
||||||
|
*
|
||||||
|
* To make sure morph movements don't look weird:
|
||||||
|
* - Follow the convention of having points start from bottom-middle and go clockwise
|
||||||
|
* - Make sure the number of points is "balanced" in all directions
|
||||||
|
* - Tip: Sometimes symmetry is not enough. Try to have more intermediate points if ones you have are too spaced out and act funny.
|
||||||
|
*/
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// To be fed
|
||||||
|
property int screenWidth: QsWindow.window.width
|
||||||
|
property int screenHeight: QsWindow.window.height
|
||||||
|
|
||||||
|
// Signals & loading
|
||||||
|
signal requestFocus()
|
||||||
|
signal dismissed()
|
||||||
|
signal focusGrabDismissed()
|
||||||
|
property bool load: true
|
||||||
|
property bool shown: true
|
||||||
|
|
||||||
|
// Some info
|
||||||
|
property int reservedTop: 0
|
||||||
|
property int reservedBottom: 0
|
||||||
|
property int reservedLeft: 0
|
||||||
|
property int reservedRight: 0
|
||||||
|
|
||||||
|
// Main stuff
|
||||||
|
property var backgroundPolygon
|
||||||
|
property list<Item> baseMaskItems: [root]
|
||||||
|
property list<Item> attachedMaskItems: []
|
||||||
|
property list<Item> maskItems: [...baseMaskItems, ...attachedMaskItems]
|
||||||
|
property Region maskRegion: Region {
|
||||||
|
regions: root.maskItems.map(item => regionComp.createObject(this, { "item": item }))
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAttachedMaskItem(item) {
|
||||||
|
if (root.attachedMaskItems.includes(item)) return;
|
||||||
|
root.attachedMaskItems.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAttachedMaskItem(item) {
|
||||||
|
root.attachedMaskItems = root.attachedMaskItems.filter(i => i !== item);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAttachedMaskItemsChanged: {
|
||||||
|
if (attachedMaskItems.length > 0) {
|
||||||
|
S.GlobalFocusGrab.addDismissable(root.QsWindow.window);
|
||||||
|
} else {
|
||||||
|
S.GlobalFocusGrab.removeDismissable(root.QsWindow.window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: S.GlobalFocusGrab
|
||||||
|
function onDismissed() {
|
||||||
|
root.attachedMaskItems = [];
|
||||||
|
root.focusGrabDismissed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: regionComp
|
||||||
|
Region {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import qs
|
||||||
|
import qs.modules.common
|
||||||
|
import "../../common/widgets/shapes/material-shapes.js" as MaterialShapes
|
||||||
|
import "../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding
|
||||||
|
import "../../common/widgets/shapes/geometry/offset.js" as Offset
|
||||||
|
|
||||||
|
HAbstractMorphedPanel {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// Own props
|
||||||
|
property int edgeGap: Appearance.sizes.hyprlandGapsOut
|
||||||
|
property real rounding: Appearance.rounding.windowRounding
|
||||||
|
property real contentHeight: 300 // For now
|
||||||
|
property real contentWidth: root.screenWidth * 0.9
|
||||||
|
property real horizontalGap: (root.screenWidth - contentWidth) / 2
|
||||||
|
|
||||||
|
// Background
|
||||||
|
backgroundPolygon: {
|
||||||
|
const bottom = Config.options.bar.bottom
|
||||||
|
const topY = bottom ? (root.screenHeight - edgeGap - contentHeight) : edgeGap
|
||||||
|
const bottomY = bottom ? (root.screenHeight - edgeGap) : (edgeGap + contentHeight)
|
||||||
|
const points = [
|
||||||
|
// bottom-middle
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, bottomY), new CornerRounding.CornerRounding(0)),
|
||||||
|
// bottom-left
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap + rounding, bottomY), new CornerRounding.CornerRounding(rounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, bottomY), new CornerRounding.CornerRounding(rounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, bottomY - rounding), new CornerRounding.CornerRounding(rounding)),
|
||||||
|
// top-left
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, topY + rounding), new CornerRounding.CornerRounding(rounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap, topY), new CornerRounding.CornerRounding(rounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(horizontalGap + rounding, topY), new CornerRounding.CornerRounding(rounding)),
|
||||||
|
// top-middle
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth / 2, topY), new CornerRounding.CornerRounding(0)),
|
||||||
|
// top-right
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap - rounding, topY), new CornerRounding.CornerRounding(rounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, topY), new CornerRounding.CornerRounding(rounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, topY + rounding), new CornerRounding.CornerRounding(rounding)),
|
||||||
|
|
||||||
|
// bottom-right
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, bottomY - rounding), new CornerRounding.CornerRounding(rounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap, bottomY), new CornerRounding.CornerRounding(rounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(root.screenWidth - horizontalGap - rounding, bottomY), new CornerRounding.CornerRounding(rounding)),
|
||||||
|
]
|
||||||
|
return MaterialShapes.customPolygon(points, 1, new Offset.Offset(root.screenWidth / 2, edgeGap + contentHeight / 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Keybinds
|
||||||
|
// GlobalShortcut {
|
||||||
|
// name: "searchToggle"
|
||||||
|
// description: "Toggles search on press"
|
||||||
|
|
||||||
|
// onPressed: {
|
||||||
|
// GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// GlobalShortcut {
|
||||||
|
// name: "searchToggleRelease"
|
||||||
|
// description: "Toggles search on release"
|
||||||
|
|
||||||
|
// onPressed: {
|
||||||
|
// GlobalStates.superReleaseMightTrigger = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// onReleased: {
|
||||||
|
// if (!GlobalStates.superReleaseMightTrigger) {
|
||||||
|
// GlobalStates.superReleaseMightTrigger = true;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// GlobalShortcut {
|
||||||
|
// name: "searchToggleReleaseInterrupt"
|
||||||
|
// description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything."
|
||||||
|
|
||||||
|
// onPressed: {
|
||||||
|
// GlobalStates.superReleaseMightTrigger = false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Connections {
|
||||||
|
// target: GlobalStates
|
||||||
|
// function onOverviewOpenChanged() {
|
||||||
|
// if (GlobalStates.overviewOpen) {
|
||||||
|
// root.requestFocus();
|
||||||
|
// } else {
|
||||||
|
// root.dismissed();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
// The stuff that sits on the "top" layer for layershells. Not to be confused with "toplevels" as in windows.
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: Quickshell.screens
|
||||||
|
delegate: HTopLayerPanel {
|
||||||
|
required property var modelData
|
||||||
|
screen: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import qs
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import "../../common"
|
||||||
|
import "../../common/widgets/shapes" as S
|
||||||
|
import "../../common/widgets/shapes/material-shapes.js" as MaterialShapes
|
||||||
|
import "../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding
|
||||||
|
import "../../common/widgets/shapes/geometry/offset.js" as Offset
|
||||||
|
|
||||||
|
import "bar"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fullscreen layer. Uses masking to not block clicks on windows n' stuff.
|
||||||
|
*/
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
///////////////// Window //////////////////
|
||||||
|
property real opacity: 1 * (GlobalStates.barOpen && !GlobalStates.screenLocked)
|
||||||
|
Behavior on opacity {
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
color: "transparent"
|
||||||
|
WlrLayershell.namespace: "quickshell:topLayerPanel"
|
||||||
|
exclusionMode: ExclusionMode.Ignore
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
bottom: true
|
||||||
|
}
|
||||||
|
|
||||||
|
mask: root.currentPanel.maskRegion
|
||||||
|
// HyprlandWindow.visibleMask: mask // TODO: use this later to optimize hyprland's rendering
|
||||||
|
|
||||||
|
///////////////// Focus dim //////////////////
|
||||||
|
|
||||||
|
FadeLoader {
|
||||||
|
z: 0
|
||||||
|
anchors.fill: parent
|
||||||
|
shown: GlobalFocusGrab.active
|
||||||
|
sourceComponent: Rectangle {
|
||||||
|
opacity: 0.2
|
||||||
|
color: Appearance.m3colors.m3scrim
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////// Content //////////////////
|
||||||
|
|
||||||
|
property alias roundedPolygon: backgroundShape.roundedPolygon
|
||||||
|
property bool finishedMorphing: true
|
||||||
|
onRoundedPolygonChanged: finishedMorphing = false
|
||||||
|
Connections {
|
||||||
|
target: backgroundShape
|
||||||
|
function onProgressChanged() {
|
||||||
|
// While it overshoots because of the spring animation, waiting for the bounce to finish entirely would be too slow
|
||||||
|
// ^ (totally not an excuse for my laziness)
|
||||||
|
if (backgroundShape.progress >= 1.0) {
|
||||||
|
root.finishedMorphing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
S.ShapeCanvas {
|
||||||
|
id: backgroundShape
|
||||||
|
z: 1
|
||||||
|
anchors.fill: parent
|
||||||
|
polygonIsNormalized: false
|
||||||
|
roundedPolygon: MaterialShapes.customPolygon([new MaterialShapes.PointNRound(new Offset.Offset(root.screen.width, 0), new CornerRounding.CornerRounding(9999)),])
|
||||||
|
animation: NumberAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial
|
||||||
|
}
|
||||||
|
color: Appearance.colors.colLayer0
|
||||||
|
opacity: root.opacity
|
||||||
|
borderWidth: (root.currentPanel === bar && Config.options.bar.cornerStyle !== 1) ? 0 : 1
|
||||||
|
borderColor: Appearance.colors.colLayer0Border
|
||||||
|
visible: false // cuz there's already the shadow
|
||||||
|
// debug: true
|
||||||
|
}
|
||||||
|
DropShadow {
|
||||||
|
id: shadow
|
||||||
|
opacity: root.opacity
|
||||||
|
source: backgroundShape
|
||||||
|
anchors.fill: backgroundShape
|
||||||
|
radius: 10
|
||||||
|
samples: radius * 2 + 1 // Ideally radius * 2 + 1, see qt docs
|
||||||
|
color: "#44000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
property HAbstractMorphedPanel currentPanel: null
|
||||||
|
Component.onCompleted: currentPanel = bar
|
||||||
|
roundedPolygon: currentPanel.backgroundPolygon
|
||||||
|
|
||||||
|
// Do we want to have reserved area always follow the bar or maybe differ per panel?
|
||||||
|
EdgeReservedArea {
|
||||||
|
anchors.top: true
|
||||||
|
exclusiveZone: bar.reservedTop
|
||||||
|
}
|
||||||
|
EdgeReservedArea {
|
||||||
|
anchors.bottom: true
|
||||||
|
exclusiveZone: bar.reservedBottom
|
||||||
|
}
|
||||||
|
EdgeReservedArea {
|
||||||
|
anchors.left: true
|
||||||
|
exclusiveZone: bar.reservedLeft
|
||||||
|
}
|
||||||
|
EdgeReservedArea {
|
||||||
|
anchors.right: true
|
||||||
|
exclusiveZone: bar.reservedRight
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////// Content: Panels ///////////////
|
||||||
|
|
||||||
|
function dismiss() {
|
||||||
|
root.currentPanel = bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: panelRootItem
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: GlobalFocusGrab
|
||||||
|
function onActiveChanged() {
|
||||||
|
if (GlobalFocusGrab.active) {
|
||||||
|
panelRootItem.focus = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Keys.onPressed: (event) => { // Esc to close
|
||||||
|
if (event.key === Qt.Key_Escape) {
|
||||||
|
GlobalFocusGrab.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HBar {
|
||||||
|
id: bar
|
||||||
|
opacity: root.opacity
|
||||||
|
load: root.currentPanel === this && root.finishedMorphing // the extra condition is to prevent workspace widget from acting up when switching horizontal/vertical... should be fixed later
|
||||||
|
shown: root.finishedMorphing
|
||||||
|
}
|
||||||
|
|
||||||
|
// HOverview {
|
||||||
|
// id: overview
|
||||||
|
// opacity: root.opacity
|
||||||
|
// load: root.currentPanel === this
|
||||||
|
// shown: root.finishedMorphing
|
||||||
|
// onRequestFocus: root.currentPanel = overview
|
||||||
|
// onDismissed: root.dismiss()
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////// Components /////////////////
|
||||||
|
|
||||||
|
component EdgeReservedArea: PanelWindow {
|
||||||
|
WlrLayershell.namespace: "quickshell:edgeReservedArea"
|
||||||
|
implicitWidth: 0
|
||||||
|
implicitHeight: 0
|
||||||
|
mask: Region {
|
||||||
|
item: null
|
||||||
|
}
|
||||||
|
screen: root.screen
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import "../../../common/widgets/shapes/material-shapes.js" as MaterialShapes
|
||||||
|
import "../../../common/widgets/shapes/shapes/corner-rounding.js" as CornerRounding
|
||||||
|
import "../../../common/widgets/shapes/geometry/offset.js" as Offset
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
HAbstractMorphedPanel {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// Config
|
||||||
|
property bool vertical: Config.options.bar.vertical
|
||||||
|
property bool atBottom: Config.options.bar.bottom
|
||||||
|
property int cornerStyle: Config.options.bar.cornerStyle
|
||||||
|
|
||||||
|
// Own props
|
||||||
|
property int barHeight: Appearance.sizes.baseBarHeight
|
||||||
|
property int barVerticalWidth: Appearance.sizes.baseVerticalBarWidth
|
||||||
|
function getRounding(cornerStyle) {
|
||||||
|
switch(cornerStyle) {
|
||||||
|
case 0: return Appearance.rounding.screenRounding;
|
||||||
|
case 1: return Appearance.rounding.windowRounding;
|
||||||
|
case 2: return 0;
|
||||||
|
default: return Appearance.rounding.screenRounding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getEdgeGap(cornerStyle) {
|
||||||
|
switch(cornerStyle) {
|
||||||
|
case 0: return 0;
|
||||||
|
case 1: return Appearance.sizes.hyprlandGapsOut;
|
||||||
|
case 2: return 0;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getEdgeRounding(cornerStyle) {
|
||||||
|
switch(cornerStyle) {
|
||||||
|
case 0: return 0;
|
||||||
|
case 1: return Appearance.rounding.windowRounding;
|
||||||
|
case 2: return 0;
|
||||||
|
default: return Appearance.rounding.windowRounding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getHug(cornerStyle) {
|
||||||
|
return cornerStyle === 0;
|
||||||
|
}
|
||||||
|
property int reservedArea: (vertical ? barVerticalWidth : barHeight) + getEdgeGap(cornerStyle)
|
||||||
|
|
||||||
|
// Some info
|
||||||
|
reservedTop: (!atBottom && !vertical) ? reservedArea : 0
|
||||||
|
reservedBottom: (atBottom && !vertical) ? reservedArea : 0
|
||||||
|
reservedLeft: (!atBottom && vertical) ? reservedArea : 0
|
||||||
|
reservedRight: (atBottom && vertical) ? reservedArea : 0
|
||||||
|
|
||||||
|
// Background
|
||||||
|
function getBackgroundPolygon() {
|
||||||
|
// It's certainly cleaner to have the below props declared outside, but we do this
|
||||||
|
// to make sure a config change only makes this re-evaluate exactly once
|
||||||
|
const bottom = root.atBottom
|
||||||
|
const vertical = root.vertical
|
||||||
|
const cornerStyle = root.cornerStyle
|
||||||
|
const hug = root.getHug(cornerStyle)
|
||||||
|
const edgeGap = root.getEdgeGap(cornerStyle)
|
||||||
|
const edgeRounding = root.getEdgeRounding(cornerStyle)
|
||||||
|
const rounding = root.getRounding(cornerStyle)
|
||||||
|
|
||||||
|
const areaHeight = vertical ? root.screenHeight : (root.barHeight + edgeGap * 2)
|
||||||
|
const areaWidth = vertical ? (root.barVerticalWidth + edgeGap * 2) : root.screenWidth
|
||||||
|
const height = vertical ? (root.screenHeight - edgeGap * 2) : root.barHeight
|
||||||
|
const width = vertical ? root.barVerticalWidth : (root.screenWidth - edgeGap * 2)
|
||||||
|
|
||||||
|
const xLeft = (vertical && bottom) ? (root.screenWidth - edgeGap - width) : edgeGap
|
||||||
|
const xRight = (vertical && !bottom) ? (areaWidth - edgeGap) : (root.screenWidth - edgeGap)
|
||||||
|
const yTop = (!vertical && bottom) ? (root.screenHeight - edgeGap - height) : edgeGap
|
||||||
|
const yBottom = (!vertical && !bottom) ? (areaHeight - edgeGap) : (root.screenHeight - edgeGap)
|
||||||
|
|
||||||
|
const topLeftRounding = !bottom ? edgeRounding : rounding
|
||||||
|
const topRightRounding = !(bottom^vertical) ? edgeRounding : rounding
|
||||||
|
const bottomLeftRounding = !!(bottom^vertical) ? edgeRounding : rounding
|
||||||
|
const bottomRightRounding = bottom ? edgeRounding : rounding
|
||||||
|
|
||||||
|
var topCornerYDirection = 0, bottomCornerYDirection = 0, leftCornerXDirection = 0, rightCornerXDirection = 0;
|
||||||
|
if (vertical) {
|
||||||
|
topCornerYDirection = 1;
|
||||||
|
bottomCornerYDirection = -1;
|
||||||
|
} else if (cornerStyle === 2) { // Rect
|
||||||
|
topCornerYDirection = 0;
|
||||||
|
bottomCornerYDirection = 0;
|
||||||
|
} else if (cornerStyle === 1) { // Rounded
|
||||||
|
topCornerYDirection = 1;
|
||||||
|
bottomCornerYDirection = -1;
|
||||||
|
} else { // Hug
|
||||||
|
topCornerYDirection = bottom ? -1 : 1;
|
||||||
|
bottomCornerYDirection = bottom ? -1 : 1;
|
||||||
|
}
|
||||||
|
if (!vertical) {
|
||||||
|
leftCornerXDirection = 1;
|
||||||
|
rightCornerXDirection = -1
|
||||||
|
} else if (cornerStyle === 2) { // Rect
|
||||||
|
leftCornerXDirection = 0;
|
||||||
|
rightCornerXDirection = 0;
|
||||||
|
} else if (cornerStyle === 1) { // Rounded
|
||||||
|
leftCornerXDirection = 1;
|
||||||
|
rightCornerXDirection = -1;
|
||||||
|
} else { // Hug
|
||||||
|
leftCornerXDirection = bottom ? -1 : 1;
|
||||||
|
rightCornerXDirection = bottom ? -1 : 1;
|
||||||
|
}
|
||||||
|
var points = [
|
||||||
|
// bottom-middle
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft + width * 1/2, yBottom), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft + width * 0.1, yBottom), new CornerRounding.CornerRounding(0)),
|
||||||
|
|
||||||
|
// bottom-left
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft + rounding * leftCornerXDirection, yBottom), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yBottom), new CornerRounding.CornerRounding(bottomLeftRounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yBottom + rounding * bottomCornerYDirection), new CornerRounding.CornerRounding(edgeRounding)),
|
||||||
|
|
||||||
|
// middle-left
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop + height * 0.9), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop + height * 1/2), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop + height * 0.1), new CornerRounding.CornerRounding(0)),
|
||||||
|
|
||||||
|
// top-left
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop + rounding * topCornerYDirection), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft, yTop), new CornerRounding.CornerRounding(topLeftRounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft + rounding * leftCornerXDirection, yTop), new CornerRounding.CornerRounding(0)),
|
||||||
|
|
||||||
|
// top-middle
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft + width * 0.1, yTop), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft + width * 1/2, yTop), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft + width * 0.9, yTop), new CornerRounding.CornerRounding(0)),
|
||||||
|
|
||||||
|
// top-right
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xRight + rounding * rightCornerXDirection, yTop), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop), new CornerRounding.CornerRounding(topRightRounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop + rounding * topCornerYDirection), new CornerRounding.CornerRounding(0)),
|
||||||
|
|
||||||
|
// middle-right
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop + height * 0.1), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop + height * 1/2), new CornerRounding.CornerRounding(0)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xRight, yTop + height * 0.9), new CornerRounding.CornerRounding(0)),
|
||||||
|
|
||||||
|
// bottom-right
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xRight, yBottom + rounding * bottomCornerYDirection), new CornerRounding.CornerRounding(edgeRounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xRight, yBottom), new CornerRounding.CornerRounding(bottomRightRounding)),
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xRight + rounding * rightCornerXDirection, yBottom), new CornerRounding.CornerRounding(0)),
|
||||||
|
|
||||||
|
// bottom-middle
|
||||||
|
new MaterialShapes.PointNRound(new Offset.Offset(xLeft + width * 0.9, yBottom), new CornerRounding.CornerRounding(0)),
|
||||||
|
]
|
||||||
|
return MaterialShapes.customPolygon(points, 1, new Offset.Offset(root.screenWidth / 2, edgeGap + barHeight / 2))
|
||||||
|
}
|
||||||
|
backgroundPolygon: getBackgroundPolygon()
|
||||||
|
Connections {
|
||||||
|
target: Config
|
||||||
|
function onReadyChanged() {
|
||||||
|
if (Config.ready)
|
||||||
|
root.backgroundPolygon = root.getBackgroundPolygon()
|
||||||
|
}
|
||||||
|
function onReloaded() {
|
||||||
|
root.extraLoadCondition = false
|
||||||
|
root.backgroundPolygon = root.getBackgroundPolygon()
|
||||||
|
root.extraLoadCondition = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content
|
||||||
|
implicitHeight: vertical ? screenHeight : (barHeight + getEdgeGap(cornerStyle) * 2)
|
||||||
|
implicitWidth: vertical ? (barVerticalWidth + getEdgeGap(cornerStyle) * 2) : screenWidth
|
||||||
|
width: implicitWidth
|
||||||
|
height: implicitHeight
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: undefined
|
||||||
|
left: undefined
|
||||||
|
right: undefined
|
||||||
|
}
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "bottom"
|
||||||
|
when: root.atBottom && !root.vertical
|
||||||
|
AnchorChanges {
|
||||||
|
target: root
|
||||||
|
anchors.top: undefined
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: undefined
|
||||||
|
anchors.right: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "left"
|
||||||
|
when: !root.atBottom && root.vertical
|
||||||
|
AnchorChanges {
|
||||||
|
target: root
|
||||||
|
anchors.top: undefined
|
||||||
|
anchors.bottom: undefined
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "right"
|
||||||
|
when: root.atBottom && root.vertical
|
||||||
|
AnchorChanges {
|
||||||
|
target: root
|
||||||
|
anchors.top: undefined
|
||||||
|
anchors.bottom: undefined
|
||||||
|
anchors.left: undefined
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
transitions: Transition {
|
||||||
|
AnchorAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool extraLoadCondition: true
|
||||||
|
FadeLazyLoader {
|
||||||
|
id: contentLoader
|
||||||
|
load: root.load && root.extraLoadCondition
|
||||||
|
shown: root.shown && root.extraLoadCondition
|
||||||
|
anchors.fill: parent
|
||||||
|
component: HBarContent {
|
||||||
|
parent: contentLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.modules.common as C
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool vertical: C.Config.options.bar.vertical
|
||||||
|
property real spacing: 4
|
||||||
|
|
||||||
|
property list<var> leftWidgets: C.Config.options.hefty.bar.leftWidgets
|
||||||
|
property list<var> centerLeftWidgets: C.Config.options.hefty.bar.centerLeftWidgets
|
||||||
|
property list<var> centerWidgets: C.Config.options.hefty.bar.centerWidgets
|
||||||
|
property list<var> centerRightWidgets: C.Config.options.hefty.bar.centerRightWidgets
|
||||||
|
property list<var> rightWidgets: C.Config.options.hefty.bar.rightWidgets
|
||||||
|
|
||||||
|
Side {
|
||||||
|
id: leftSide
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.right: !root.vertical ? centerLeftSide.left : parent.right
|
||||||
|
anchors.bottom: !root.vertical ? parent.bottom : centerLeftSide.top
|
||||||
|
// For accessibility
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.topMargin: 0
|
||||||
|
|
||||||
|
HBarUserFallbackComponentRepeater {
|
||||||
|
componentNames: root.leftWidgets
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Side {
|
||||||
|
id: centerLeftSide
|
||||||
|
anchors.right: !root.vertical ? centerSide.left : parent.right
|
||||||
|
anchors.bottom: !root.vertical ? parent.bottom : centerSide.top
|
||||||
|
HBarUserFallbackComponentRepeater {
|
||||||
|
componentNames: [...root.centerLeftWidgets, ...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []),]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Side {
|
||||||
|
id: centerSide
|
||||||
|
anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined
|
||||||
|
anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined
|
||||||
|
HBarUserFallbackComponentRepeater {
|
||||||
|
componentNames: [...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), ...root.centerWidgets, ...(root.centerRightWidgets.length > 0 ? [invisibleItem] : []),]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Side {
|
||||||
|
id: centerRightSide
|
||||||
|
anchors.left: !root.vertical ? centerSide.right : parent.left
|
||||||
|
anchors.top: !root.vertical ? parent.top : centerSide.bottom
|
||||||
|
HBarUserFallbackComponentRepeater {
|
||||||
|
componentNames: [...(root.centerLeftWidgets.length > 0 ? [invisibleItem] : []), ...root.centerRightWidgets,]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Side {
|
||||||
|
id: rightSide
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: !root.vertical ? centerRightSide.right : parent.left
|
||||||
|
anchors.top: !root.vertical ? parent.top : centerRightSide.bottom
|
||||||
|
// For accessibility
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
HBarUserFallbackComponentRepeater {
|
||||||
|
componentNames: root.rightWidgets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component Side: W.BoxLayout {
|
||||||
|
anchors {
|
||||||
|
top: !root.vertical ? parent.top : undefined
|
||||||
|
bottom: !root.vertical ? parent.bottom : undefined
|
||||||
|
topMargin: root.spacing * root.vertical
|
||||||
|
bottomMargin: root.spacing * root.vertical
|
||||||
|
left: root.vertical ? parent.left : undefined
|
||||||
|
right: root.vertical ? parent.right : undefined
|
||||||
|
leftMargin: root.spacing * !root.vertical
|
||||||
|
rightMargin: root.spacing * !root.vertical
|
||||||
|
}
|
||||||
|
|
||||||
|
vertical: C.Config.options.bar.vertical
|
||||||
|
spacing: root.spacing
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.modules.common as C
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool startSide: false
|
||||||
|
property bool endSide: false
|
||||||
|
|
||||||
|
property alias color: bg.color
|
||||||
|
property real margins: 4
|
||||||
|
property real padding: 4
|
||||||
|
default property alias groupData: layout.data
|
||||||
|
|
||||||
|
readonly property bool vertical: C.Config.options.bar.vertical
|
||||||
|
readonly property bool m3eRadius: C.Config.options.hefty.bar.m3ExpressiveGrouping
|
||||||
|
readonly property real barUndirectionalWidth: C.Config.options.bar.vertical ? C.Appearance.sizes.baseVerticalBarWidth : C.Appearance.sizes.baseBarHeight
|
||||||
|
readonly property real backgroundUndirectionalWidth: barUndirectionalWidth - margins * 2
|
||||||
|
|
||||||
|
implicitWidth: vertical ? barUndirectionalWidth : layout.implicitWidth
|
||||||
|
implicitHeight: vertical ? layout.implicitHeight : barUndirectionalWidth
|
||||||
|
|
||||||
|
property alias startRadius: bg.startRadius
|
||||||
|
property alias endRadius: bg.endRadius
|
||||||
|
property alias topLeftRadius: bg.topLeftRadius
|
||||||
|
property alias topRightRadius: bg.topRightRadius
|
||||||
|
property alias bottomLeftRadius: bg.bottomLeftRadius
|
||||||
|
property alias bottomRightRadius: bg.bottomRightRadius
|
||||||
|
property real backgroundWidth: root.vertical ? root.backgroundUndirectionalWidth : (root.width - margins * (startSide + endSide))
|
||||||
|
property real backgroundHeight: !root.vertical ? root.backgroundUndirectionalWidth : (root.height - margins * (startSide + endSide))
|
||||||
|
property real backgroundTopMargin: root.margins * (!root.vertical || root.startSide)
|
||||||
|
property real backgroundLeftMargin: root.margins * (root.vertical || root.startSide)
|
||||||
|
property real fullBackgroundRadius: Math.min(backgroundWidth, backgroundHeight) / 2
|
||||||
|
function getBackgroundRadius(atSide) {
|
||||||
|
if (root.m3eRadius) {
|
||||||
|
if (atSide) return fullBackgroundRadius;
|
||||||
|
else return C.Appearance.rounding.unsharpenmore;
|
||||||
|
} else {
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property Item background: W.AxisRectangle {
|
||||||
|
id: bg
|
||||||
|
anchors {
|
||||||
|
top: parent?.top
|
||||||
|
left: parent?.left
|
||||||
|
topMargin: root.backgroundTopMargin
|
||||||
|
leftMargin: root.backgroundLeftMargin
|
||||||
|
}
|
||||||
|
contentLayer: W.StyledRectangle.ContentLayer.Group
|
||||||
|
|
||||||
|
width: root.backgroundWidth
|
||||||
|
height: root.backgroundHeight
|
||||||
|
|
||||||
|
vertical: root.vertical
|
||||||
|
startRadius: root.getBackgroundRadius(root.startSide)
|
||||||
|
endRadius: root.getBackgroundRadius(root.endSide)
|
||||||
|
}
|
||||||
|
|
||||||
|
property Item contentItem: GridLayout {
|
||||||
|
id: layout
|
||||||
|
columns: C.Config.options.bar.vertical ? 1 : -1
|
||||||
|
anchors.fill: parent
|
||||||
|
property real spacing: 4
|
||||||
|
columnSpacing: spacing
|
||||||
|
rowSpacing: spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
children: [
|
||||||
|
background,
|
||||||
|
contentItem
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import qs.modules.common as C
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
|
||||||
|
W.StyledRectangle {
|
||||||
|
id: root
|
||||||
|
contentLayer: W.StyledRectangle.ContentLayer.Pane
|
||||||
|
|
||||||
|
color: C.Appearance.colors.colLayer2Base
|
||||||
|
|
||||||
|
transitions: Transition {
|
||||||
|
AnchorAnimation {
|
||||||
|
duration: C.Appearance.animation.elementMove.duration
|
||||||
|
easing.type: C.Appearance.animation.elementMove.type
|
||||||
|
easing.bezierCurve: C.Appearance.animation.elementMove.bezierCurve
|
||||||
|
}
|
||||||
|
ColorAnimation {
|
||||||
|
duration: C.Appearance.animation.elementMoveFast.duration
|
||||||
|
easing.type: C.Appearance.animation.elementMoveFast.type
|
||||||
|
easing.bezierCurve: C.Appearance.animation.elementMoveFast.bezierCurve
|
||||||
|
}
|
||||||
|
PropertyAnimation {
|
||||||
|
properties: "topLeftRadius,topRightRadius,bottomLeftRadius,bottomRightRadius,intendedWidth,intendedHeight"
|
||||||
|
duration: C.Appearance.animation.elementMove.duration
|
||||||
|
easing.type: C.Appearance.animation.elementMove.type
|
||||||
|
easing.bezierCurve: C.Appearance.animation.elementMove.bezierCurve
|
||||||
|
}
|
||||||
|
PropertyAnimation {
|
||||||
|
properties: "opacity"
|
||||||
|
duration: C.Appearance.animation.elementMoveFast.duration
|
||||||
|
easing.type: C.Appearance.animation.elementMoveFast.type
|
||||||
|
easing.bezierCurve: C.Appearance.animation.elementMoveFast.bezierCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
W.StyledRectangularShadow {
|
||||||
|
target: root
|
||||||
|
z: -1
|
||||||
|
radius: root.topLeftRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import qs.modules.common as C
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property bool vertical: C.Config.options.bar.vertical
|
||||||
|
readonly property string invisibleItem: "_invisible"
|
||||||
|
required property list<var> componentNames
|
||||||
|
property string context: Quickshell.shellPath("modules/hefty/topLayer/bar/widgets")
|
||||||
|
|
||||||
|
model: {
|
||||||
|
const m = componentNames.map(item => {
|
||||||
|
if (item instanceof Array)
|
||||||
|
return ({"type": "container", "value": item});
|
||||||
|
else if (item === root.invisibleItem)
|
||||||
|
return ({"type": "invisible", "value": item});
|
||||||
|
else
|
||||||
|
return ({"type": "component", "value": item});
|
||||||
|
});
|
||||||
|
for (var i = 0;i < m.length; i++) {
|
||||||
|
const item = m[i];
|
||||||
|
if (item.type === "container" || item.type === "component") {
|
||||||
|
item.startSide = (i === 0) || (m[i - 1].type !== m[i].type);
|
||||||
|
item.endSide = (i + 1 >= m.length) || (m[i + 1].type !== m[i].type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// print(JSON.stringify(m, null, 2));
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
delegate: DelegateChooser {
|
||||||
|
role: "type"
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: root.invisibleItem
|
||||||
|
delegate: Item { visible: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "component"
|
||||||
|
delegate: W.UserFallbackLoader {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
componentName: modelData.value
|
||||||
|
context: root.context
|
||||||
|
property bool startSide: index === 0
|
||||||
|
property bool endSide: index === root.model.length - 1
|
||||||
|
|
||||||
|
Layout.fillWidth: root.vertical ? true : item.Layout.fillWidth
|
||||||
|
Layout.fillHeight: !root.vertical ? true : item.Layout.fillHeight
|
||||||
|
Layout.maximumWidth: item.Layout.maximumWidth
|
||||||
|
Layout.maximumHeight: item.Layout.maximumHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "container"
|
||||||
|
delegate: HBarGroupContainer {
|
||||||
|
id: group
|
||||||
|
required property var modelData
|
||||||
|
startSide: modelData.startSide
|
||||||
|
endSide: modelData.endSide
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: containerRepeater
|
||||||
|
model: group.modelData.value
|
||||||
|
delegate: W.UserFallbackLoader {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
componentName: modelData
|
||||||
|
context: root.context
|
||||||
|
property bool startSide: index === 0
|
||||||
|
property bool endSide: index === group.modelData.value.length - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.modules.common as C
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
|
||||||
|
HBarGroupContainer {
|
||||||
|
startSide: parent.startSide ?? true
|
||||||
|
endSide: parent.endSide ?? true
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import qs.modules.common as C
|
||||||
|
import qs.modules.common.functions as F
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
W.ButtonMouseArea {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property bool vertical
|
||||||
|
required property bool atBottom
|
||||||
|
required property bool showPopup
|
||||||
|
|
||||||
|
readonly property var layoutParent: F.ObjectUtils.findParentWithProperty(root, "startSide")
|
||||||
|
readonly property real layoutParentTopLeftRadius: layoutParent.topLeftRadius
|
||||||
|
readonly property real layoutParentTopRightRadius: layoutParent.topRightRadius
|
||||||
|
readonly property real layoutParentBottomLeftRadius: layoutParent.bottomLeftRadius
|
||||||
|
readonly property real layoutParentBottomRightRadius: layoutParent.bottomRightRadius
|
||||||
|
|
||||||
|
readonly property real barThickness: vertical ? C.Appearance.sizes.verticalBarWidth : C.Appearance.sizes.barHeight
|
||||||
|
readonly property real barVisualThickness: vertical ? C.Appearance.sizes.baseVerticalBarWidth : C.Appearance.sizes.baseBarHeight
|
||||||
|
readonly property real barGap: (barThickness - barVisualThickness) / 2
|
||||||
|
required property real contentImplicitWidth
|
||||||
|
required property real contentImplicitHeight
|
||||||
|
property real parentRadiusToPaddingRatio: 0.3
|
||||||
|
implicitWidth: {
|
||||||
|
if (vertical) {
|
||||||
|
return barThickness;
|
||||||
|
} else {
|
||||||
|
const roundingPadding = (layoutParentTopLeftRadius + layoutParentBottomRightRadius) * parentRadiusToPaddingRatio;
|
||||||
|
return (contentImplicitWidth + roundingPadding + 4 * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
implicitHeight: {
|
||||||
|
if (!vertical) {
|
||||||
|
return barThickness;
|
||||||
|
} else {
|
||||||
|
const roundingPadding = (layoutParentTopLeftRadius + layoutParentBottomRightRadius) * parentRadiusToPaddingRatio;
|
||||||
|
return (contentImplicitHeight + roundingPadding + 4 * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: vertical
|
||||||
|
Layout.fillHeight: !vertical
|
||||||
|
|
||||||
|
property alias hover: hoverOverlay.hover
|
||||||
|
property alias press: hoverOverlay.press
|
||||||
|
|
||||||
|
W.StateOverlay {
|
||||||
|
id: hoverOverlay
|
||||||
|
anchors.fill: parent
|
||||||
|
property real parentMargins: 4 + root.barGap
|
||||||
|
property real ownMargins: 2
|
||||||
|
property real edgeMargins: parentMargins + ownMargins
|
||||||
|
property real sideMargins: 2
|
||||||
|
anchors {
|
||||||
|
leftMargin: (root.vertical ? edgeMargins : sideMargins) + parentMargins * (!root.vertical && root.layoutParent.startSide)
|
||||||
|
rightMargin: (root.vertical ? edgeMargins : sideMargins) + parentMargins * (!root.vertical && root.layoutParent.endSide)
|
||||||
|
topMargin: (root.vertical ? sideMargins : edgeMargins) + parentMargins * (root.vertical && root.layoutParent.startSide)
|
||||||
|
bottomMargin: (root.vertical ? sideMargins : edgeMargins) + parentMargins * (root.vertical && root.layoutParent.endSide)
|
||||||
|
}
|
||||||
|
topLeftRadius: root.layoutParentTopLeftRadius - ownMargins
|
||||||
|
topRightRadius: root.layoutParentTopRightRadius - ownMargins
|
||||||
|
bottomLeftRadius: root.layoutParentBottomLeftRadius - ownMargins
|
||||||
|
bottomRightRadius: root.layoutParentBottomRightRadius - ownMargins
|
||||||
|
|
||||||
|
hover: root.containsMouse
|
||||||
|
press: root.containsPress
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,260 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
import qs.modules.common as C
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
import qs.modules.common.widgets.shapes as Shapes
|
||||||
|
import "../../../common/widgets/shapes/material-shapes.js" as MaterialShapes
|
||||||
|
|
||||||
|
// Notes
|
||||||
|
// vertical + atBottom = right side
|
||||||
|
// start radius = top or left, end radius = bottom or right
|
||||||
|
Shapes.ShapeCanvas {
|
||||||
|
id: bgShape
|
||||||
|
|
||||||
|
// Stuff fed from outside
|
||||||
|
required property bool vertical
|
||||||
|
required property bool atBottom
|
||||||
|
required property bool showPopup
|
||||||
|
required property real backgroundWidth
|
||||||
|
required property real backgroundHeight
|
||||||
|
property real popupContentWidth: 400
|
||||||
|
property real popupContentHeight: 500
|
||||||
|
property real popupPadding: 10
|
||||||
|
property real popupWidth: popupContentWidth + popupPadding * 2
|
||||||
|
property real popupHeight: popupContentHeight + popupPadding * 2
|
||||||
|
required property real startRadius
|
||||||
|
required property real endRadius
|
||||||
|
property real baseMargin: {
|
||||||
|
if (!vertical)
|
||||||
|
return parent.anchors.topMargin
|
||||||
|
else
|
||||||
|
return parent.anchors.leftMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
property alias containerShape: containerShape
|
||||||
|
property alias popupShape: popupShape
|
||||||
|
|
||||||
|
// Popup constraints
|
||||||
|
// mapToGlobal is not reactive so we gotta hook manually
|
||||||
|
property real xInGlobal
|
||||||
|
property real yInGlobal
|
||||||
|
function updateXInGlobal() {
|
||||||
|
xInGlobal = mapToGlobal(0, 0).x + xOffset;
|
||||||
|
}
|
||||||
|
function updateYInGlobal() {
|
||||||
|
yInGlobal = mapToGlobal(0, 0).y + yOffset;
|
||||||
|
}
|
||||||
|
function updatePosInGlobal() {
|
||||||
|
updateXInGlobal()
|
||||||
|
updateYInGlobal()
|
||||||
|
}
|
||||||
|
Component.onCompleted: updatePosInGlobal()
|
||||||
|
onXChanged: updatePosInGlobal()
|
||||||
|
onYChanged: updatePosInGlobal()
|
||||||
|
readonly property real minPopupXOffset: -xInGlobal
|
||||||
|
readonly property real minPopupYOffset: -yInGlobal
|
||||||
|
readonly property real maxPopupXOffset: {
|
||||||
|
const maxPopupX = QsWindow.window.screen.width - popupWidth;
|
||||||
|
const maxOffset = maxPopupX - xInGlobal;
|
||||||
|
return maxOffset;
|
||||||
|
}
|
||||||
|
readonly property real maxPopupYOffset: {
|
||||||
|
const maxPopupY = QsWindow.window.screen.height - popupHeight;
|
||||||
|
const maxOffset = maxPopupY - yInGlobal;
|
||||||
|
return maxOffset;
|
||||||
|
}
|
||||||
|
property bool lockPopupX: false
|
||||||
|
property bool lockPopupY: false
|
||||||
|
readonly property real popupXOffset: {
|
||||||
|
// print("popupXOffset", popupXOffset, "lock", lockPopupX)
|
||||||
|
if (bgShape.lockPopupX) return;
|
||||||
|
if (!vertical) return Math.min(Math.max(-(popupWidth - containerShape.width) / 2, minPopupXOffset), maxPopupXOffset);
|
||||||
|
else return atBottom ? -(popupShape.width + spacing) : (containerShape.width + spacing);
|
||||||
|
if (bgShape.showPopup) lockPopupX = true;
|
||||||
|
}
|
||||||
|
onPopupXOffsetChanged: if (bgShape.showPopup) lockPopupX = true;
|
||||||
|
readonly property real popupYOffset: {
|
||||||
|
if (bgShape.lockPopupY) return;
|
||||||
|
if (!vertical) return atBottom ? -(popupShape.height + spacing) : (containerShape.height + spacing);
|
||||||
|
else return Math.min(Math.max(-(popupHeight - containerShape.height) / 2, minPopupYOffset), maxPopupYOffset)
|
||||||
|
if (bgShape.showPopup) lockPopupY = true;
|
||||||
|
}
|
||||||
|
onPopupYOffsetChanged: if (bgShape.showPopup) lockPopupY = true;
|
||||||
|
|
||||||
|
// Positioning
|
||||||
|
readonly property real popupContentOffsetBase: popupPadding
|
||||||
|
readonly property real popupContentOffsetBaseX: popupContentOffsetBase + parent.anchors.leftMargin
|
||||||
|
readonly property real popupContentOffsetBaseY: popupContentOffsetBase + parent.anchors.topMargin
|
||||||
|
readonly property real paddedContainerHeight: containerShape.height
|
||||||
|
readonly property real paddedContainerWidth: containerShape.width
|
||||||
|
readonly property real popupContentOffsetX: {
|
||||||
|
if (!vertical) return popupXOffset + popupContentOffsetBaseX;
|
||||||
|
else return paddedContainerWidth + spacing + popupContentOffsetBaseX + (atBottom * -(popupWidth + backgroundWidth + spacing * 2));
|
||||||
|
}
|
||||||
|
readonly property real popupContentOffsetY: {
|
||||||
|
if (!vertical) return paddedContainerHeight + spacing + popupContentOffsetBaseY + (atBottom * -(popupHeight + backgroundHeight + spacing * 2))
|
||||||
|
else return popupYOffset + popupContentOffsetBaseY;
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: {
|
||||||
|
if (!vertical) return -xOffset;
|
||||||
|
if (!bgShape.atBottom || !bgShape.showPopup) return 0;
|
||||||
|
return -popupShape.width - bgShape.spacing;
|
||||||
|
}
|
||||||
|
top: parent.top
|
||||||
|
topMargin: {
|
||||||
|
if (vertical) return -yOffset;
|
||||||
|
if (!bgShape.atBottom || !bgShape.showPopup) return 0;
|
||||||
|
return -popupShape.height - bgShape.spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
width: {
|
||||||
|
if (!vertical) return bgShape.showPopup ? Math.max(backgroundWidth, popupWidth) : backgroundWidth;
|
||||||
|
else return bgShape.showPopup ? (containerShape.width + popupShape.width + bgShape.spacing) : containerShape.width;
|
||||||
|
}
|
||||||
|
height: {
|
||||||
|
if (!vertical) return bgShape.showPopup ? (containerShape.height + popupShape.height + bgShape.spacing) : containerShape.height;
|
||||||
|
else return bgShape.showPopup ? Math.max(backgroundHeight, popupHeight) : backgroundHeight;
|
||||||
|
}
|
||||||
|
property bool popupHiding: false
|
||||||
|
onShowPopupChanged: popupHiding = true
|
||||||
|
onProgressChanged: {
|
||||||
|
if (progress == 1) {
|
||||||
|
popupHiding = false
|
||||||
|
if (!showPopup) {
|
||||||
|
lockPopupX = false;
|
||||||
|
lockPopupY = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color: showPopup || popupHiding ? C.Appearance.colors.colLayer3Base : C.Appearance.colors.colLayer1
|
||||||
|
xOffset: {
|
||||||
|
if (!vertical) return showPopup ? Math.max(-popupXOffset, 0) : 0;
|
||||||
|
else return bgShape.atBottom ? (width - containerShape.width) : 0;
|
||||||
|
}
|
||||||
|
yOffset: {
|
||||||
|
if (!vertical) return bgShape.atBottom ? (height - containerShape.height) : 0;
|
||||||
|
else return showPopup ? Math.max(-popupYOffset, 0) : 0;
|
||||||
|
}
|
||||||
|
animation: Anim {}
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
Anim {}
|
||||||
|
}
|
||||||
|
Behavior on height {
|
||||||
|
Anim {}
|
||||||
|
}
|
||||||
|
Behavior on anchors.topMargin {
|
||||||
|
animation: !bgShape.vertical ? animComp.createObject(this) : undefined
|
||||||
|
}
|
||||||
|
Behavior on anchors.leftMargin {
|
||||||
|
animation: bgShape.vertical ? animComp.createObject(this) : undefined
|
||||||
|
}
|
||||||
|
Behavior on xOffset {
|
||||||
|
animation: !bgShape.vertical ? animComp.createObject(this) : undefined
|
||||||
|
}
|
||||||
|
Behavior on yOffset {
|
||||||
|
animation: bgShape.vertical ? animComp.createObject(this) : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
polygonIsNormalized: false
|
||||||
|
property real spacing: baseMargin * 2
|
||||||
|
W.AxisRectangularContainerShape {
|
||||||
|
id: containerShape
|
||||||
|
width: bgShape.backgroundWidth
|
||||||
|
height: bgShape.backgroundHeight
|
||||||
|
startRadius: bgShape.startRadius
|
||||||
|
endRadius: bgShape.endRadius
|
||||||
|
vertical: bgShape.vertical
|
||||||
|
}
|
||||||
|
W.RectangularContainerShape {
|
||||||
|
id: popupShape
|
||||||
|
width: bgShape.popupWidth
|
||||||
|
height: bgShape.popupHeight
|
||||||
|
radius: C.Appearance.rounding.large
|
||||||
|
xOffset: bgShape.popupXOffset
|
||||||
|
yOffset: bgShape.popupYOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
roundedPolygon: {
|
||||||
|
var points = [];
|
||||||
|
if (!bgShape.showPopup) return containerShape.getFullShape();
|
||||||
|
|
||||||
|
const joinRadiusOverride = containerShape.radiusLimit;
|
||||||
|
if (!bgShape.vertical) {
|
||||||
|
const popupBigger = bgShape.popupWidth > bgShape.backgroundWidth;
|
||||||
|
// Inline comment spam to mitigate qmlls' sabotaging of the (code) layout
|
||||||
|
points = [
|
||||||
|
...(bgShape.atBottom ? containerShape.getFirstBottomPoints() : [ //
|
||||||
|
...popupShape.getFirstBottomPoints(), popupShape.getBottomLeftPoint(), //
|
||||||
|
...popupShape.leftPoints, //
|
||||||
|
popupShape.getTopLeftPoint(0, -bgShape.spacing * (!popupBigger), joinRadiusOverride), //
|
||||||
|
]), //
|
||||||
|
containerShape.getBottomLeftPoint(0, bgShape.spacing * (!bgShape.atBottom && popupBigger), joinRadiusOverride), //
|
||||||
|
containerShape.getTopLeftPoint(0, -bgShape.spacing * (bgShape.atBottom && popupBigger), joinRadiusOverride), //
|
||||||
|
...(!bgShape.atBottom ? containerShape.topPoints : [ //
|
||||||
|
popupShape.getBottomLeftPoint(0, bgShape.spacing * (!popupBigger), joinRadiusOverride), //
|
||||||
|
...popupShape.leftPoints, //
|
||||||
|
popupShape.getTopLeftPoint(), //
|
||||||
|
...popupShape.topPoints, //
|
||||||
|
popupShape.getTopRightPoint(), //
|
||||||
|
...popupShape.rightPoints, //
|
||||||
|
popupShape.getBottomRightPoint(0, bgShape.spacing * (!popupBigger), joinRadiusOverride), //
|
||||||
|
]), //
|
||||||
|
containerShape.getTopRightPoint(0, -bgShape.spacing * (bgShape.atBottom && popupBigger), joinRadiusOverride), //
|
||||||
|
containerShape.getBottomRightPoint(0, bgShape.spacing * (!bgShape.atBottom && popupBigger), joinRadiusOverride), //
|
||||||
|
...(bgShape.atBottom ? containerShape.getLastBottomPoints() : [ //
|
||||||
|
popupShape.getTopRightPoint(0, -bgShape.spacing * (!popupBigger), joinRadiusOverride), //
|
||||||
|
...popupShape.rightPoints, //
|
||||||
|
popupShape.getBottomRightPoint(), //
|
||||||
|
...popupShape.getLastBottomPoints(), //
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
const popupBigger = bgShape.popupHeight > bgShape.backgroundHeight;
|
||||||
|
points = [ //
|
||||||
|
...containerShape.getFirstBottomPoints(), //
|
||||||
|
containerShape.getBottomLeftPoint(-bgShape.spacing * (popupBigger && bgShape.atBottom), 0, joinRadiusOverride), //
|
||||||
|
...(!bgShape.atBottom ? containerShape.leftPoints : [ //
|
||||||
|
popupShape.getBottomRightPoint(bgShape.spacing * (!popupBigger)), //
|
||||||
|
...popupShape.bottomPoints, //
|
||||||
|
popupShape.getBottomLeftPoint(), //
|
||||||
|
...popupShape.leftPoints, //
|
||||||
|
popupShape.getTopLeftPoint(), //
|
||||||
|
...popupShape.topPoints, //
|
||||||
|
popupShape.getTopRightPoint(bgShape.spacing * (!popupBigger)), //
|
||||||
|
]), //
|
||||||
|
containerShape.getTopLeftPoint(-bgShape.spacing * (popupBigger && bgShape.atBottom), 0, joinRadiusOverride), //
|
||||||
|
...containerShape.topPoints, //
|
||||||
|
containerShape.getTopRightPoint(bgShape.spacing * (popupBigger && !bgShape.atBottom), 0, joinRadiusOverride), //
|
||||||
|
...(bgShape.atBottom ? containerShape.rightPoints : [ //
|
||||||
|
popupShape.getTopLeftPoint(-bgShape.spacing * (!popupBigger), 0, joinRadiusOverride), //
|
||||||
|
...popupShape.topPoints, //
|
||||||
|
popupShape.getTopRightPoint(), //
|
||||||
|
...popupShape.rightPoints, //
|
||||||
|
popupShape.getBottomRightPoint(), //
|
||||||
|
...popupShape.bottomPoints, //
|
||||||
|
popupShape.getBottomLeftPoint(-bgShape.spacing * (!popupBigger), 0, joinRadiusOverride), //
|
||||||
|
]), //
|
||||||
|
containerShape.getBottomRightPoint(bgShape.spacing * (popupBigger && !bgShape.atBottom), 0, joinRadiusOverride), //
|
||||||
|
...containerShape.getLastBottomPoints(), //
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return MaterialShapes.customPolygon(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
component Anim: NumberAnimation {
|
||||||
|
duration: C.Appearance.animation.elementMove.duration
|
||||||
|
easing.type: C.Appearance.animation.elementMove.type
|
||||||
|
easing.bezierCurve: C.Appearance.animation.elementMove.bezierCurve
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: animComp
|
||||||
|
Anim {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import qs.modules.common as C
|
||||||
|
import qs.modules.common.functions as F
|
||||||
|
import qs.services as S
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
HBarWidgetContainer {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool showPopup: false
|
||||||
|
readonly property bool vertical: C.Config.options.bar.vertical
|
||||||
|
readonly property bool atBottom: C.Config.options.bar.bottom
|
||||||
|
|
||||||
|
// Interactions
|
||||||
|
property var morphedPanelParent: F.ObjectUtils.findParentWithProperty(root, "maskItems")
|
||||||
|
onShowPopupChanged: {
|
||||||
|
if (root.showPopup) {
|
||||||
|
root.morphedPanelParent.addAttachedMaskItem(bgShape);
|
||||||
|
} else {
|
||||||
|
root.morphedPanelParent.removeAttachedMaskItem(bgShape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: root.morphedPanelParent
|
||||||
|
function onFocusGrabDismissed() {
|
||||||
|
root.showPopup = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background container shape
|
||||||
|
property alias backgroundShape: bgShape
|
||||||
|
property alias popupContentWidth: bgShape.popupContentWidth
|
||||||
|
property alias popupContentHeight: bgShape.popupContentHeight
|
||||||
|
property alias popupContentOffsetX: bgShape.popupContentOffsetX
|
||||||
|
property alias popupContentOffsetY: bgShape.popupContentOffsetY
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
topMargin: root.backgroundTopMargin
|
||||||
|
leftMargin: root.backgroundLeftMargin
|
||||||
|
}
|
||||||
|
implicitWidth: root.backgroundWidth
|
||||||
|
implicitHeight: root.backgroundHeight
|
||||||
|
|
||||||
|
HBarWidgetShapeBackground {
|
||||||
|
id: bgShape
|
||||||
|
|
||||||
|
vertical: root.vertical
|
||||||
|
atBottom: root.atBottom
|
||||||
|
showPopup: root.showPopup
|
||||||
|
|
||||||
|
backgroundWidth: root.backgroundWidth
|
||||||
|
backgroundHeight: root.backgroundHeight
|
||||||
|
startRadius: root.getBackgroundRadius(root.startSide)
|
||||||
|
endRadius: root.getBackgroundRadius(root.endSide)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
import qs
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
HBarWidgetWithPopout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool showPing: false
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Ai
|
||||||
|
function onResponseFinished() {
|
||||||
|
if (GlobalStates.sidebarLeftOpen) return;
|
||||||
|
root.showPing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Booru
|
||||||
|
function onResponseFinished() {
|
||||||
|
if (GlobalStates.sidebarLeftOpen) return;
|
||||||
|
root.showPing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: GlobalStates
|
||||||
|
function onSidebarLeftOpenChanged() {
|
||||||
|
root.showPing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HBarWidgetContent {
|
||||||
|
id: contentRoot
|
||||||
|
parentRadiusToPaddingRatio: 0
|
||||||
|
vertical: root.vertical
|
||||||
|
atBottom: root.atBottom
|
||||||
|
contentImplicitWidth: 14
|
||||||
|
contentImplicitHeight: 14
|
||||||
|
implicitWidth: 40
|
||||||
|
implicitHeight: 46
|
||||||
|
showPopup: false
|
||||||
|
onClicked: GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
|
||||||
|
|
||||||
|
CustomIcon {
|
||||||
|
id: distroIcon
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 20
|
||||||
|
height: 20
|
||||||
|
source: Config.options.bar.topLeftIcon == 'distro' ? SystemInfo.distroIcon : `${Config.options.bar.topLeftIcon}-symbolic`
|
||||||
|
colorize: true
|
||||||
|
color: Appearance.colors.colOnLayer0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
opacity: root.showPing ? 1 : 0
|
||||||
|
visible: opacity > 0
|
||||||
|
anchors {
|
||||||
|
bottom: parent.bottom
|
||||||
|
right: parent.right
|
||||||
|
bottomMargin: -2
|
||||||
|
rightMargin: -2
|
||||||
|
}
|
||||||
|
implicitWidth: 8
|
||||||
|
implicitHeight: 8
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
color: Appearance.colors.colTertiary
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
import qs
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
HBarWidgetWithPopout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
HBarWidgetContent {
|
||||||
|
id: contentRoot
|
||||||
|
vertical: root.vertical
|
||||||
|
atBottom: root.atBottom
|
||||||
|
contentImplicitWidth: symbol.implicitWidth
|
||||||
|
contentImplicitHeight: symbol.implicitHeight
|
||||||
|
showPopup: false
|
||||||
|
onClicked: GlobalStates.sessionOpen = true;
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
id: symbol
|
||||||
|
anchors.centerIn: parent
|
||||||
|
iconSize: 20
|
||||||
|
text: "power_settings_new"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,598 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Services.UPower
|
||||||
|
|
||||||
|
import qs.services as S
|
||||||
|
import qs.modules.common as C
|
||||||
|
import qs.modules.common.functions as F
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
HBarWidgetWithPopout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool chargingAndNotFull: S.Battery.isCharging && S.Battery.percentage < 1
|
||||||
|
property bool powerSaving: PowerProfiles.profile == PowerProfile.PowerSaver
|
||||||
|
|
||||||
|
popupContentWidth: popupContent.implicitWidth
|
||||||
|
popupContentHeight: popupContent.implicitHeight
|
||||||
|
|
||||||
|
HBarWidgetContent {
|
||||||
|
id: contentRoot
|
||||||
|
|
||||||
|
vertical: root.vertical
|
||||||
|
atBottom: root.atBottom
|
||||||
|
showPopup: root.showPopup
|
||||||
|
onClicked: root.showPopup = !root.showPopup
|
||||||
|
|
||||||
|
contentImplicitWidth: sysInfoContent.implicitWidth
|
||||||
|
contentImplicitHeight: sysInfoContent.implicitHeight
|
||||||
|
|
||||||
|
parentRadiusToPaddingRatio: 0
|
||||||
|
|
||||||
|
SysInfoContent {
|
||||||
|
id: sysInfoContent
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
SysInfoPopupContent {
|
||||||
|
id: popupContent
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
topMargin: root.popupContentOffsetY
|
||||||
|
leftMargin: root.popupContentOffsetX
|
||||||
|
}
|
||||||
|
|
||||||
|
shown: root.showPopup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component SysInfoContent: Item {
|
||||||
|
implicitWidth: contentGrid.implicitWidth + contentGrid.anchors.leftMargin + contentGrid.anchors.rightMargin
|
||||||
|
implicitHeight: contentGrid.implicitHeight + contentGrid.anchors.topMargin + contentGrid.anchors.bottomMargin
|
||||||
|
|
||||||
|
W.BoxLayout {
|
||||||
|
id: contentGrid
|
||||||
|
vertical: root.vertical
|
||||||
|
property int visibleChildren: children.filter(child => child.shown).length
|
||||||
|
property bool hasResourceIndicators: visibleChildren > 1 || (visibleChildren > 0 && !S.Battery.available)
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
leftMargin: !root.vertical ? (hasResourceIndicators ? 1 : 4) : 0
|
||||||
|
topMargin: root.vertical ? 2 : 0
|
||||||
|
rightMargin: !root.vertical ? (root.endSide ? 1 : (battLoader.visible ? 0 : -3)) : 0
|
||||||
|
bottomMargin: root.vertical ? (root.endSide ? 4 : 2) : 0
|
||||||
|
}
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
Item {} // Trick for extra spacing
|
||||||
|
Item {
|
||||||
|
visible: root.startSide
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignedFadeLoader {
|
||||||
|
shown: C.Config.options.hefty.bar.resources.showMemory || (
|
||||||
|
!battLoader.visible
|
||||||
|
&& !C.Config.options.hefty.bar.resources.showRam
|
||||||
|
&& !C.Config.options.hefty.bar.resources.showSwap
|
||||||
|
&& !C.Config.options.hefty.bar.resources.showCpu
|
||||||
|
)
|
||||||
|
sourceComponent: Memory {}
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignedFadeLoader {
|
||||||
|
shown: C.Config.options.hefty.bar.resources.showRam
|
||||||
|
sourceComponent: RamOnly {}
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignedFadeLoader {
|
||||||
|
shown: C.Config.options.hefty.bar.resources.showSwap
|
||||||
|
sourceComponent: SwapOnly {}
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignedFadeLoader {
|
||||||
|
shown: C.Config.options.hefty.bar.resources.showCpu
|
||||||
|
sourceComponent: Cpu {}
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignedFadeLoader {
|
||||||
|
id: battLoader
|
||||||
|
shown: S.Battery.available
|
||||||
|
Layout.fillWidth: root.vertical
|
||||||
|
Layout.fillHeight: !root.vertical
|
||||||
|
sourceComponent: Battery {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component AlignedFadeLoader: W.FadeLoader {
|
||||||
|
Layout.alignment: root.vertical ? Qt.AlignHCenter : Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
component Memory: SysResourceProgress {
|
||||||
|
valueWeights: [S.ResourceUsage.memoryTotal, S.ResourceUsage.swapTotal]
|
||||||
|
values: [S.ResourceUsage.memoryUsedPercentage, S.ResourceUsage.swapUsedPercentage]
|
||||||
|
centerChar: S.Translation.tr("Memory")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
component RamOnly: SysResourceProgress {
|
||||||
|
values: [S.ResourceUsage.memoryUsedPercentage]
|
||||||
|
centerChar: S.Translation.tr("RAM")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
component SwapOnly: SysResourceProgress {
|
||||||
|
values: [S.ResourceUsage.swapUsedPercentage]
|
||||||
|
centerChar: S.Translation.tr("Swap")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
component Cpu: SysResourceProgress {
|
||||||
|
values: [S.ResourceUsage.cpuUsage]
|
||||||
|
centerChar: S.Translation.tr("CPU")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
component SysResourceProgress: W.CombinedCircularProgress {
|
||||||
|
id: sysResProg
|
||||||
|
implicitSize: 22
|
||||||
|
valueHighlights: [C.Appearance.colors.colPrimary]
|
||||||
|
valueTroughs: [C.Appearance.colors.colSecondaryContainer]
|
||||||
|
property string centerChar: ""
|
||||||
|
|
||||||
|
W.StyledText {
|
||||||
|
renderType: Text.QtRendering
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: sysResProg.centerChar
|
||||||
|
font.pixelSize: 9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component Battery: Item {
|
||||||
|
implicitWidth: !root.vertical ? battShape.implicitWidth : battShape.implicitHeight
|
||||||
|
implicitHeight: !root.vertical ? battShape.implicitHeight : battShape.implicitWidth
|
||||||
|
|
||||||
|
BatteryShape {
|
||||||
|
id: battShape
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component BatteryShape: Row {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
spacing: 1.5
|
||||||
|
rotation: -90 * root.vertical
|
||||||
|
|
||||||
|
W.ClippedProgressBar {
|
||||||
|
id: batteryProgress
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
valueBarWidth: 28
|
||||||
|
valueBarHeight: 16
|
||||||
|
radius: 4
|
||||||
|
progressRadius: 0
|
||||||
|
value: S.Battery.percentage
|
||||||
|
highlightColor: (S.Battery.isLow && !S.Battery.isCharging) ? C.Appearance.m3colors.m3error : C.Appearance.colors.colOnSecondaryContainer
|
||||||
|
font.pixelSize: boltIcon.visible ? 13 : 14
|
||||||
|
|
||||||
|
Item {
|
||||||
|
layer.enabled: true
|
||||||
|
width: batteryProgress.valueBarWidth
|
||||||
|
height: batteryProgress.valueBarHeight
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: (parent.height - height) / 2
|
||||||
|
}
|
||||||
|
rotation: 180 * root.vertical
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
W.MaterialSymbol {
|
||||||
|
id: boltIcon
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.leftMargin: -2
|
||||||
|
Layout.rightMargin: -2
|
||||||
|
rotation: -90 * root.vertical
|
||||||
|
fill: 1 * (text == "bolt")
|
||||||
|
fillAnimation: null
|
||||||
|
text: {
|
||||||
|
if (batteryProgress.value == 1)
|
||||||
|
return "check";
|
||||||
|
if (root.chargingAndNotFull)
|
||||||
|
return "bolt";
|
||||||
|
if (root.powerSaving)
|
||||||
|
return "nest_eco_leaf";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
iconSize: C.Appearance.font.pixelSize.small
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
visible: text != ""
|
||||||
|
}
|
||||||
|
W.VisuallyCenteredStyledText {
|
||||||
|
visible: batteryProgress.value < 1
|
||||||
|
Layout.fillHeight: true
|
||||||
|
rotation: -90 * root.vertical
|
||||||
|
font: batteryProgress.font
|
||||||
|
text: batteryProgress.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: batteryProgress.trackColor
|
||||||
|
topRightRadius: width
|
||||||
|
bottomRightRadius: width
|
||||||
|
implicitWidth: 2.5
|
||||||
|
implicitHeight: 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component SysInfoPopupContent: W.ChoreographerLoader {
|
||||||
|
id: sysInfoPopupContent
|
||||||
|
|
||||||
|
sourceComponent: W.ChoreographerGridLayout {
|
||||||
|
id: popupRoot
|
||||||
|
rowSpacing: 10
|
||||||
|
property bool showSettings: false
|
||||||
|
|
||||||
|
onShownChanged: {
|
||||||
|
if (shown) {
|
||||||
|
powerProfileSelection.focusSelectedChild();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
W.FlyFadeEnterChoreographable {
|
||||||
|
z: 1
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Item {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
implicitHeight: popupHeaderLayout.implicitHeight
|
||||||
|
ColumnLayout {
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: popupHeaderLayout
|
||||||
|
Layout.fillWidth: true
|
||||||
|
W.StyledText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: S.Translation.tr("Resources")
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pixelSize: C.Appearance.font.pixelSize.title
|
||||||
|
}
|
||||||
|
|
||||||
|
W.StyledIconButton {
|
||||||
|
implicitSize: C.Appearance.rounding.normal * 2
|
||||||
|
text: "settings"
|
||||||
|
iconSize: 20
|
||||||
|
onClicked: popupRoot.showSettings = !popupRoot.showSettings;
|
||||||
|
checked: popupRoot.showSettings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
W.FadeLoader {
|
||||||
|
shown: popupRoot.showSettings
|
||||||
|
Layout.fillWidth: true
|
||||||
|
sourceComponent: SysInfoPopupConfig {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
W.FlyFadeEnterChoreographable {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
implicitHeight: memUsed.implicitHeight
|
||||||
|
|
||||||
|
BigSmallTextPair {
|
||||||
|
id: memUsed
|
||||||
|
materialSymbol: "memory"
|
||||||
|
bigText: S.ResourceUsage.kbToGbString(S.ResourceUsage.memoryUsed, false)
|
||||||
|
smallText: {
|
||||||
|
const total = S.ResourceUsage.kbToGbString(S.ResourceUsage.memoryTotal, false);
|
||||||
|
return S.Translation.tr("%1").arg(`/ ${total}`)
|
||||||
|
}
|
||||||
|
W.StyledText {
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
text: S.Translation.tr("Memory")
|
||||||
|
color: C.Appearance.colors.colOutline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BigSmallTextPair {
|
||||||
|
id: swapUsed
|
||||||
|
TextMetrics {
|
||||||
|
id: plusTextMetric
|
||||||
|
font: swapUsed.bigFont
|
||||||
|
text: "+"
|
||||||
|
}
|
||||||
|
property real halfWidthOfAPlus: plusTextMetric.width / 2
|
||||||
|
x: Math.min(memProg.availableWidth * memProg.visualEnds[0] - halfWidthOfAPlus, parent.width - width)
|
||||||
|
bigText: "+ " + S.ResourceUsage.kbToGbString(S.ResourceUsage.swapUsed, false)
|
||||||
|
smallText: {
|
||||||
|
const total = S.ResourceUsage.kbToGbString(S.ResourceUsage.swapTotal, false);
|
||||||
|
return `/ ${total} GB`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
W.StyledCombinedProgressBar {
|
||||||
|
id: memProg
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
valueWeights: [S.ResourceUsage.memoryTotal, S.ResourceUsage.swapTotal]
|
||||||
|
values: [S.ResourceUsage.memoryUsedPercentage, S.ResourceUsage.swapUsedPercentage]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
W.FlyFadeEnterChoreographable {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
BigSmallTextPair {
|
||||||
|
spacing: 0
|
||||||
|
materialSymbol: "developer_board"
|
||||||
|
bigText: Math.round(S.ResourceUsage.cpuUsage * 100)
|
||||||
|
smallText: "%"
|
||||||
|
W.StyledText {
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
text: " " + S.Translation.tr("CPU")
|
||||||
|
color: C.Appearance.colors.colOutline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
W.StyledCombinedProgressBar {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
property bool useSingleAggregate: S.ResourceUsage.cpuCoreUsages.length > 8
|
||||||
|
valueWeights: useSingleAggregate ? [1] : S.ResourceUsage.cpuCoreFreqCaps
|
||||||
|
values: useSingleAggregate ? [S.ResourceUsage.cpuUsage] : S.ResourceUsage.cpuCoreUsages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
W.FlyFadeEnterChoreographable {
|
||||||
|
Layout.topMargin: 8
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 10
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
W.CircularProgress {
|
||||||
|
id: battCircProg
|
||||||
|
implicitSize: 44
|
||||||
|
lineWidth: 3
|
||||||
|
value: S.Battery.percentage
|
||||||
|
W.MaterialSymbol {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
iconSize: 22
|
||||||
|
fill: 1
|
||||||
|
animateChange: true
|
||||||
|
text: {
|
||||||
|
if (battCircProg.value == 1)
|
||||||
|
return "check";
|
||||||
|
if (root.chargingAndNotFull)
|
||||||
|
return "bolt";
|
||||||
|
if (root.powerSaving)
|
||||||
|
return "energy_savings_leaf";
|
||||||
|
if (PowerProfiles.profile == PowerProfile.Performance)
|
||||||
|
return "local_fire_department";
|
||||||
|
return C.Icons.getBatteryIcon(battCircProg.value * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: false
|
||||||
|
spacing: 4
|
||||||
|
W.StyledText {
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
visible: S.Battery.knownEnergyRate
|
||||||
|
text: F.DateUtils.formatDuration(S.Battery.isCharging ? S.Battery.timeToFull : S.Battery.timeToEmpty)
|
||||||
|
font.pixelSize: C.Appearance.font.pixelSize.title
|
||||||
|
}
|
||||||
|
W.StyledText {
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
text: {
|
||||||
|
if (!S.Battery.knownEnergyRate)
|
||||||
|
return S.Battery.isCharging ? S.Translation.tr("Charging") : S.Translation.tr("Discharging");
|
||||||
|
return S.Battery.isCharging ? S.Translation.tr("to full") : S.Translation.tr("remaining");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
id: notSoImportantBatteryStats
|
||||||
|
Layout.fillWidth: false
|
||||||
|
spacing: 4
|
||||||
|
StatWithIcon {
|
||||||
|
visible: S.Battery.knownEnergyRate
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
icon: "bolt"
|
||||||
|
value: `${S.Battery.energyRate.toFixed(1)}W`
|
||||||
|
longestValueString: "69.0W"
|
||||||
|
}
|
||||||
|
StatWithIcon {
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
icon: "favorite"
|
||||||
|
value: `${(S.Battery.health).toFixed(1 * (S.Battery.health < 100))}%`
|
||||||
|
longestValueString: "69.0%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
W.FlyFadeEnterChoreographable {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
W.ConfigSelectionArray {
|
||||||
|
id: powerProfileSelection
|
||||||
|
currentValue: PowerProfiles.profile
|
||||||
|
onSelected: newValue => {
|
||||||
|
PowerProfiles.profile = newValue;
|
||||||
|
}
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: S.Translation.tr("Power saver"),
|
||||||
|
value: PowerProfile.PowerSaver
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: S.Translation.tr("Balanced"),
|
||||||
|
value: PowerProfile.Balanced
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: S.Translation.tr("Performance"),
|
||||||
|
value: PowerProfile.Performance
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component SysInfoPopupConfig: Rectangle {
|
||||||
|
implicitWidth: sysConfLayout.implicitWidth
|
||||||
|
implicitHeight: sysConfLayout.implicitHeight
|
||||||
|
radius: C.Appearance.rounding.normal
|
||||||
|
color: C.Appearance.colors.colLayer4Base
|
||||||
|
|
||||||
|
W.StyledRectangularShadow {
|
||||||
|
target: parent
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: sysConfLayout
|
||||||
|
anchors.fill: parent
|
||||||
|
W.ConfigSwitch {
|
||||||
|
buttonIcon: "pie_chart"
|
||||||
|
text: S.Translation.tr("Show memory usage")
|
||||||
|
checked: C.Config.options.hefty.bar.resources.showMemory
|
||||||
|
onCheckedChanged: {
|
||||||
|
C.Config.options.hefty.bar.resources.showMemory = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
W.ConfigSwitch {
|
||||||
|
buttonIcon: "memory"
|
||||||
|
text: S.Translation.tr("Show RAM usage")
|
||||||
|
checked: C.Config.options.hefty.bar.resources.showRam
|
||||||
|
onCheckedChanged: {
|
||||||
|
C.Config.options.hefty.bar.resources.showRam = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
W.ConfigSwitch {
|
||||||
|
buttonIcon: "swap_horiz"
|
||||||
|
text: S.Translation.tr("Show swap")
|
||||||
|
checked: C.Config.options.hefty.bar.resources.showSwap
|
||||||
|
onCheckedChanged: {
|
||||||
|
C.Config.options.hefty.bar.resources.showSwap = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
W.ConfigSwitch {
|
||||||
|
buttonIcon: "planner_review"
|
||||||
|
text: S.Translation.tr("Show CPU usage")
|
||||||
|
checked: C.Config.options.hefty.bar.resources.showCpu
|
||||||
|
onCheckedChanged: {
|
||||||
|
C.Config.options.hefty.bar.resources.showCpu = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component BigSmallTextPair: RowLayout {
|
||||||
|
id: txtPair
|
||||||
|
property string materialSymbol: ""
|
||||||
|
property string bigText: ""
|
||||||
|
property string smallText: ""
|
||||||
|
property alias bigFont: bigTxt.font
|
||||||
|
property alias smallFont: smallTxt.font
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
W.MaterialSymbol {
|
||||||
|
Layout.rightMargin: 6 - spacing
|
||||||
|
visible: text.length > 0
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
text: txtPair.materialSymbol
|
||||||
|
fill: 1
|
||||||
|
iconSize: 24
|
||||||
|
}
|
||||||
|
W.StyledText {
|
||||||
|
id: bigTxt
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
font.pixelSize: C.Appearance.font.pixelSize.title
|
||||||
|
text: txtPair.bigText
|
||||||
|
}
|
||||||
|
W.StyledText {
|
||||||
|
id: smallTxt
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
text: txtPair.smallText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component StatWithIcon: Item {
|
||||||
|
id: statItem
|
||||||
|
required property string icon
|
||||||
|
required property string value
|
||||||
|
property string longestValueString
|
||||||
|
implicitWidth: statRow.implicitWidth
|
||||||
|
implicitHeight: statRow.implicitHeight
|
||||||
|
RowLayout {
|
||||||
|
id: statRow
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 4
|
||||||
|
W.MaterialSymbol {
|
||||||
|
Layout.fillWidth: false
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
text: statItem.icon
|
||||||
|
fill: 1
|
||||||
|
iconSize: 16
|
||||||
|
}
|
||||||
|
W.FixedWidthTextContainer {
|
||||||
|
longestText: statItem.longestValueString
|
||||||
|
W.VisuallyCenteredStyledText {
|
||||||
|
anchors.fill: parent
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
text: statItem.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Services.UPower
|
||||||
|
|
||||||
|
import qs
|
||||||
|
import qs.services as S
|
||||||
|
import qs.modules.common as C
|
||||||
|
import qs.modules.common.functions as F
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
HBarWidgetWithPopout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool chargingAndNotFull: S.Battery.isCharging && S.Battery.percentage < 1
|
||||||
|
property bool powerSaving: PowerProfiles.profile == PowerProfile.PowerSaver
|
||||||
|
|
||||||
|
popupContentWidth: popupContent.implicitWidth
|
||||||
|
popupContentHeight: popupContent.implicitHeight
|
||||||
|
|
||||||
|
HBarWidgetContent {
|
||||||
|
id: contentRoot
|
||||||
|
vertical: root.vertical
|
||||||
|
atBottom: root.atBottom
|
||||||
|
showPopup: root.showPopup
|
||||||
|
contentImplicitWidth: activeItem.implicitWidth
|
||||||
|
contentImplicitHeight: activeItem.implicitHeight
|
||||||
|
onClicked: GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; // TODO: use own popup
|
||||||
|
property var activeItem: content
|
||||||
|
|
||||||
|
SystemIndicatorsContent {
|
||||||
|
id: content
|
||||||
|
anchors {
|
||||||
|
top: !root.vertical ? parent.top : undefined
|
||||||
|
bottom: !root.vertical ? parent.bottom : undefined
|
||||||
|
left: root.vertical ? parent.left : undefined
|
||||||
|
right: root.vertical ? parent.right : undefined
|
||||||
|
horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined
|
||||||
|
verticalCenter: root.vertical ? parent.verticalCenter : undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemPanel {
|
||||||
|
id: popupContent
|
||||||
|
anchors {
|
||||||
|
top: root.vertical ? contentRoot.activeItem.top : contentRoot.activeItem.top
|
||||||
|
topMargin: root.popupContentOffsetY
|
||||||
|
left: root.vertical ? contentRoot.activeItem.left : contentRoot.activeItem.left
|
||||||
|
leftMargin: root.popupContentOffsetX
|
||||||
|
}
|
||||||
|
|
||||||
|
shown: root.showPopup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component SystemIndicatorsContent: W.BoxLayout {
|
||||||
|
vertical: root.vertical
|
||||||
|
rowSpacing: 2
|
||||||
|
columnSpacing: 8
|
||||||
|
|
||||||
|
VolMuteIcon {}
|
||||||
|
MicMuteIcon {}
|
||||||
|
WifiIcon {}
|
||||||
|
BluetoothIcon {}
|
||||||
|
}
|
||||||
|
|
||||||
|
component VolMuteIcon: IconIndicator {
|
||||||
|
visible: S.Audio.sink?.audio?.muted ?? false
|
||||||
|
W.MaterialSymbol {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "volume_off"
|
||||||
|
iconSize: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component MicMuteIcon: IconIndicator {
|
||||||
|
visible: S.Audio.source?.audio?.muted ?? false
|
||||||
|
W.MaterialSymbol {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "mic_off"
|
||||||
|
iconSize: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component BluetoothIcon: IconIndicator {
|
||||||
|
visible: S.BluetoothStatus.available
|
||||||
|
child: W.MaterialSymbol {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
iconSize: 20
|
||||||
|
text: S.BluetoothStatus.connected ? "bluetooth_connected" : S.BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component WifiIcon: IconIndicator {
|
||||||
|
child: W.MaterialSymbol {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: C.Icons.getNetworkMaterialSymbol()
|
||||||
|
iconSize: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component IconIndicator: Item {
|
||||||
|
Layout.fillWidth: root.vertical
|
||||||
|
Layout.fillHeight: !root.vertical
|
||||||
|
default property Item child
|
||||||
|
implicitWidth: child.implicitWidth
|
||||||
|
implicitHeight: child.implicitHeight
|
||||||
|
children: [child]
|
||||||
|
}
|
||||||
|
|
||||||
|
component SystemPanel: W.ChoreographerLoader {
|
||||||
|
sourceComponent: W.ChoreographerGridLayout {
|
||||||
|
id: panelRoot
|
||||||
|
|
||||||
|
W.FlyFadeEnterChoreographable {
|
||||||
|
W.StyledText {
|
||||||
|
text: "five little chuddies jumping on da bed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
W.FlyFadeEnterChoreographable {
|
||||||
|
W.StyledText {
|
||||||
|
text: "one fell off and bumped his head"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
W.FlyFadeEnterChoreographable {
|
||||||
|
W.StyledText {
|
||||||
|
text: "momma call the doctor and the doctor sez"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.ii.bar
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
HBarWidgetWithPopout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property list<var> pinnedItems: TrayService.pinnedItems
|
||||||
|
property list<var> unpinnedItems: TrayService.unpinnedItems
|
||||||
|
|
||||||
|
popupContentWidth: popupContent.implicitWidth
|
||||||
|
popupContentHeight: popupContent.implicitHeight
|
||||||
|
|
||||||
|
Layout.maximumWidth: vertical ? -1 : implicitWidth
|
||||||
|
Layout.maximumHeight: vertical ? implicitHeight : -1
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
HBarWidgetContent {
|
||||||
|
id: contentRoot
|
||||||
|
|
||||||
|
vertical: root.vertical
|
||||||
|
atBottom: root.atBottom
|
||||||
|
showPopup: root.showPopup
|
||||||
|
|
||||||
|
contentImplicitWidth: trayContent.implicitWidth
|
||||||
|
contentImplicitHeight: trayContent.implicitHeight
|
||||||
|
|
||||||
|
hoverEnabled: false
|
||||||
|
parentRadiusToPaddingRatio: 0.45
|
||||||
|
|
||||||
|
hover: trayContent.moreHovered
|
||||||
|
press: trayContent.morePressed
|
||||||
|
|
||||||
|
TrayContent {
|
||||||
|
id: trayContent
|
||||||
|
anchors.fill: parent
|
||||||
|
vertical: root.vertical
|
||||||
|
}
|
||||||
|
|
||||||
|
UnpinnedItemsPopup {
|
||||||
|
id: popupContent
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: root.popupContentOffsetY
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: root.popupContentOffsetX
|
||||||
|
}
|
||||||
|
shown: root.showPopup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component TrayContent: BoxLayout {
|
||||||
|
spacing: 4
|
||||||
|
property alias moreHovered: moreBtn.containsMouse
|
||||||
|
property alias morePressed: moreBtn.containsPress
|
||||||
|
|
||||||
|
ButtonMouseArea {
|
||||||
|
id: moreBtn
|
||||||
|
visible: TrayService.unpinnedItems.length > 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.topMargin: 4 * root.vertical
|
||||||
|
Layout.leftMargin: 4 * !root.vertical
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.AllButtons
|
||||||
|
|
||||||
|
implicitWidth: 20 - parent.spacing
|
||||||
|
implicitHeight: 20 - parent.spacing
|
||||||
|
|
||||||
|
onClicked: root.showPopup = !root.showPopup
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
iconSize: 20
|
||||||
|
text: "expand_more"
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: root.showPopup ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer2
|
||||||
|
rotation: (root.showPopup ? 180 : 0) - (90 * root.vertical) + (180 * root.atBottom)
|
||||||
|
Behavior on rotation {
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Repeater {
|
||||||
|
model: root.pinnedItems
|
||||||
|
delegate: StyledTrayItem {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
required property var modelData
|
||||||
|
item: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component UnpinnedItemsPopup: ChoreographerLoader {
|
||||||
|
sourceComponent: ChoreographerGridLayout {
|
||||||
|
id: popupRoot
|
||||||
|
totalDuration: 70
|
||||||
|
columns: root.vertical ? 1 : -1
|
||||||
|
columnSpacing: 8
|
||||||
|
rowSpacing: 8
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.unpinnedItems
|
||||||
|
delegate: FlyFadeEnterChoreographable {
|
||||||
|
id: unpinnedTrayItem
|
||||||
|
required property var modelData
|
||||||
|
StyledTrayItem {
|
||||||
|
item: unpinnedTrayItem.modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component StyledTrayItem: SysTrayItem {
|
||||||
|
iconSize: 18
|
||||||
|
propagateComposedEvents: false
|
||||||
|
|
||||||
|
property var menuWindow
|
||||||
|
onMenuClosed: {
|
||||||
|
if (menuWindow)
|
||||||
|
GlobalFocusGrab.removeDismissable(menuWindow);
|
||||||
|
}
|
||||||
|
onMenuOpened: (qsWindow) => {
|
||||||
|
menuWindow = qsWindow;
|
||||||
|
GlobalFocusGrab.addDismissable(qsWindow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import qs.modules.common as C
|
||||||
|
import qs.services as S
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
HBarWidgetWithPopout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property string timeFormatString: C.Config.options.time.format
|
||||||
|
readonly property bool is12h: timeFormatString.startsWith("h:")
|
||||||
|
readonly property bool hasAmPm: timeFormatString.toLowerCase().includes("ap") || timeFormatString.toLowerCase().endsWith("a")
|
||||||
|
readonly property bool capitalizedAmPm: timeFormatString.includes("AP") || timeFormatString.endsWith("A")
|
||||||
|
|
||||||
|
popupContentWidth: popupContent.implicitWidth
|
||||||
|
popupContentHeight: popupContent.implicitHeight
|
||||||
|
|
||||||
|
// The button on the bar
|
||||||
|
HBarWidgetContent {
|
||||||
|
id: contentRoot
|
||||||
|
|
||||||
|
vertical: root.vertical
|
||||||
|
atBottom: root.atBottom
|
||||||
|
showPopup: root.showPopup
|
||||||
|
|
||||||
|
onClicked: root.showPopup = !showPopup
|
||||||
|
contentImplicitWidth: activeItem.implicitWidth
|
||||||
|
contentImplicitHeight: activeItem.implicitHeight
|
||||||
|
|
||||||
|
property Item activeItem: vertical ? verticalContent : horizontalContent
|
||||||
|
|
||||||
|
// When horizontal
|
||||||
|
Loader {
|
||||||
|
id: horizontalContent
|
||||||
|
anchors.fill: parent
|
||||||
|
active: !contentRoot.vertical
|
||||||
|
sourceComponent: HorizontalClock {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When vertical
|
||||||
|
Loader {
|
||||||
|
id: verticalContent
|
||||||
|
anchors.fill: parent
|
||||||
|
active: contentRoot.vertical
|
||||||
|
sourceComponent: VerticalClock {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Popup content
|
||||||
|
PopupContent {
|
||||||
|
id: popupContent
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
topMargin: root.popupContentOffsetY
|
||||||
|
leftMargin: root.popupContentOffsetX
|
||||||
|
}
|
||||||
|
|
||||||
|
shown: root.showPopup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component HorizontalClock: Item {
|
||||||
|
implicitWidth: contentLayout.implicitWidth
|
||||||
|
implicitHeight: contentLayout.implicitHeight
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: contentLayout
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
W.FixedWidthTextContainer {
|
||||||
|
Layout.leftMargin: contentRoot.layoutParentTopLeftRadius * contentRoot.parentRadiusToPaddingRatio
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||||
|
Layout.fillHeight: true
|
||||||
|
longestText: Qt.locale().toString(new Date(1984, 6, 9, 00, 00, 00), root.timeFormatString)
|
||||||
|
font: clockText.font
|
||||||
|
W.VisuallyCenteredStyledText {
|
||||||
|
id: clockText
|
||||||
|
anchors.fill: parent
|
||||||
|
font.pixelSize: C.Appearance.font.pixelSize.large
|
||||||
|
color: C.Appearance.colors.colOnLayer1
|
||||||
|
text: S.DateTime.time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
W.VisuallyCenteredStyledText {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
font.pixelSize: C.Appearance.font.pixelSize.small
|
||||||
|
color: C.Appearance.colors.colOnLayer1
|
||||||
|
text: "•"
|
||||||
|
}
|
||||||
|
|
||||||
|
W.VisuallyCenteredStyledText {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
font.pixelSize: C.Appearance.font.pixelSize.small
|
||||||
|
color: C.Appearance.colors.colOnLayer1
|
||||||
|
text: S.DateTime.longDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component VerticalClock: Item {
|
||||||
|
implicitWidth: contentLayoutVertical.implicitWidth
|
||||||
|
implicitHeight: contentLayoutVertical.implicitHeight
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: contentLayoutVertical
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: amPmText.visible ? -2 : -4
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: verticalTime
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: -4
|
||||||
|
|
||||||
|
W.StyledText {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: C.Appearance.font.pixelSize.large
|
||||||
|
color: C.Appearance.colors.colOnLayer1
|
||||||
|
text: {
|
||||||
|
var hrs = S.DateTime.clock.hours;
|
||||||
|
if (root.is12h && hrs != 12)
|
||||||
|
hrs %= 12;
|
||||||
|
return hrs.toString().padStart(2, '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
W.StyledText {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: C.Appearance.font.pixelSize.large
|
||||||
|
color: C.Appearance.colors.colOnLayer1
|
||||||
|
text: S.DateTime.clock.minutes.toString().padStart(2, '0')
|
||||||
|
}
|
||||||
|
W.StyledText {
|
||||||
|
id: amPmText
|
||||||
|
visible: root.hasAmPm
|
||||||
|
Layout.topMargin: -2
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: C.Appearance.font.pixelSize.smaller
|
||||||
|
color: C.Appearance.colors.colOnLayer1
|
||||||
|
text: Qt.locale().toString(S.DateTime.clock.date, root.capitalizedAmPm ? "AP" : "ap")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
W.StyledText {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: C.Appearance.font.pixelSize.smallest
|
||||||
|
color: C.Appearance.colors.colOnLayer1
|
||||||
|
text: S.DateTime.shortDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component PopupContent: W.ChoreographerLoader {
|
||||||
|
sourceComponent: W.ChoreographerGridLayout {
|
||||||
|
id: popupRoot
|
||||||
|
|
||||||
|
property real buttonSize: C.Appearance.rounding.normal * 2
|
||||||
|
property real buttonSpacing: 4
|
||||||
|
|
||||||
|
rowSpacing: 2
|
||||||
|
|
||||||
|
W.FlyFadeEnterChoreographable {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.bottomMargin: 6
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
W.StyledText {
|
||||||
|
Layout.leftMargin: 6
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: calendarView.title
|
||||||
|
font.pixelSize: C.Appearance.font.pixelSize.larger
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: C.Appearance.colors.colSecondary
|
||||||
|
}
|
||||||
|
W.StyledIconButton {
|
||||||
|
implicitSize: 30
|
||||||
|
text: "chevron_left"
|
||||||
|
iconSize: 20
|
||||||
|
onClicked: calendarView.scrollMonthsAndSnap(-1)
|
||||||
|
colForeground: C.Appearance.colors.colPrimary
|
||||||
|
}
|
||||||
|
W.StyledIconButton {
|
||||||
|
implicitSize: 30
|
||||||
|
text: "chevron_right"
|
||||||
|
iconSize: 20
|
||||||
|
onClicked: calendarView.scrollMonthsAndSnap(1)
|
||||||
|
colForeground: C.Appearance.colors.colPrimary
|
||||||
|
}
|
||||||
|
W.StyledIconButton {
|
||||||
|
implicitSize: 30
|
||||||
|
text: "rotate_left"
|
||||||
|
iconSize: 20
|
||||||
|
onClicked: calendarView.scrollToToday()
|
||||||
|
colForeground: C.Appearance.colors.colPrimary
|
||||||
|
enabled: calendarView.targetWeekDiff != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
W.FlyFadeEnterChoreographable {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
W.CalendarDaysOfWeek {
|
||||||
|
locale: calendarView.locale
|
||||||
|
spacing: popupRoot.buttonSpacing
|
||||||
|
delegate: Item {
|
||||||
|
id: dowItem
|
||||||
|
required property var model
|
||||||
|
implicitWidth: popupRoot.buttonSize
|
||||||
|
implicitHeight: dowText.implicitHeight
|
||||||
|
|
||||||
|
W.VisuallyCenteredStyledText {
|
||||||
|
id: dowText
|
||||||
|
anchors.centerIn: parent
|
||||||
|
font.pixelSize: C.Appearance.font.pixelSize.smaller
|
||||||
|
color: C.Appearance.colors.colOutline
|
||||||
|
text: {
|
||||||
|
var result = dowItem.model.shortName;
|
||||||
|
if (C.Config.options.calendar.force2CharDayOfWeek)
|
||||||
|
result = result.substring(0, 2);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
W.FlyFadeEnterChoreographable {
|
||||||
|
Item {
|
||||||
|
implicitWidth: calendarView.implicitWidth - calendarView.horizontalPadding * 2
|
||||||
|
implicitHeight: calendarView.implicitHeight - calendarView.verticalPadding * 2
|
||||||
|
W.CalendarView {
|
||||||
|
id: calendarView
|
||||||
|
anchors.centerIn: parent
|
||||||
|
locale: Qt.locale(C.Config.options.calendar.locale)
|
||||||
|
verticalPadding: 4
|
||||||
|
horizontalPadding: 4
|
||||||
|
buttonSize: popupRoot.buttonSize
|
||||||
|
buttonSpacing: popupRoot.buttonSpacing
|
||||||
|
buttonVerticalSpacing: popupRoot.buttonSpacing
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
delegate: W.StyledButton {
|
||||||
|
id: dayButton
|
||||||
|
required property var model
|
||||||
|
|
||||||
|
focus: model.today
|
||||||
|
checked: model.today
|
||||||
|
enabled: model.month === calendarView.focusedMonth
|
||||||
|
implicitWidth: popupRoot.buttonSize
|
||||||
|
implicitHeight: popupRoot.buttonSize
|
||||||
|
width: implicitWidth
|
||||||
|
height: implicitHeight
|
||||||
|
text: model.day
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: popupRoot
|
||||||
|
enabled: dayButton.model.today
|
||||||
|
function onShownChanged() {
|
||||||
|
if (popupRoot.shown)
|
||||||
|
dayButton.forceActiveFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
W.VisuallyCenteredStyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: dayButton.text
|
||||||
|
color: dayButton.colForeground
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
HBarWidgetWithPopout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen)
|
||||||
|
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||||
|
readonly property var activeHyprlandClient: HyprlandData.clientForToplevel(activeWindow)
|
||||||
|
readonly property bool focusingThisMonitor: HyprlandData.activeWorkspace?.monitor == monitor?.name
|
||||||
|
readonly property var biggestWindow: HyprlandData.biggestWindowForWorkspace(HyprlandData.monitors[root.monitor?.id]?.activeWorkspace.id)
|
||||||
|
readonly property bool hasFocusedWindow: (focusingThisMonitor && activeWindow?.activated && biggestWindow) ?? false
|
||||||
|
|
||||||
|
readonly property string primaryText: {
|
||||||
|
if (root.hasFocusedWindow)
|
||||||
|
return root.activeWindow?.title;
|
||||||
|
return (root.biggestWindow?.title) ?? `${Translation.tr("Workspace")} ${root.monitor?.activeWorkspace?.id ?? 1}`;
|
||||||
|
}
|
||||||
|
readonly property string secondaryText: {
|
||||||
|
if (root.hasFocusedWindow && root.activeWindow?.appId != "" && root.activeWindow?.appId != primaryText)
|
||||||
|
return root.activeWindow?.appId;
|
||||||
|
return Translation.tr("Options")
|
||||||
|
}
|
||||||
|
onPrimaryTextChanged: showPopup = false;
|
||||||
|
|
||||||
|
property real fontPixelSize: Appearance.font.pixelSize.smaller
|
||||||
|
|
||||||
|
Layout.maximumWidth: vertical ? -1 : implicitWidth
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
popupContentWidth: popupContent.implicitWidth
|
||||||
|
popupContentHeight: popupContent.implicitHeight
|
||||||
|
|
||||||
|
HBarWidgetContent {
|
||||||
|
id: contentRoot
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
vertical: root.vertical
|
||||||
|
atBottom: root.atBottom
|
||||||
|
contentImplicitWidth: winTitleContent.implicitWidth
|
||||||
|
contentImplicitHeight: winTitleContent.implicitHeight
|
||||||
|
showPopup: false
|
||||||
|
onClicked: root.showPopup = !root.showPopup;
|
||||||
|
|
||||||
|
WinTitleContent {
|
||||||
|
id: winTitleContent
|
||||||
|
}
|
||||||
|
|
||||||
|
WinOptionsPopup {
|
||||||
|
id: popupContent
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: root.popupContentOffsetY
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: root.popupContentOffsetX
|
||||||
|
}
|
||||||
|
shown: root.showPopup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component WinTitleContent: BoxLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
vertical: root.vertical
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.leftMargin: 4 * !root.vertical
|
||||||
|
Layout.topMargin: 3 * root.vertical
|
||||||
|
Layout.bottomMargin: 4 * root.vertical
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
implicitWidth: appIcon.implicitWidth
|
||||||
|
implicitHeight: appIcon.implicitHeight
|
||||||
|
|
||||||
|
AppIcon {
|
||||||
|
id: appIcon
|
||||||
|
anchors.centerIn: parent
|
||||||
|
opacity: 0
|
||||||
|
source: Quickshell.iconPath(AppSearch.guessIcon(root.activeWindow?.appId), "image-missing")
|
||||||
|
implicitSize: 16
|
||||||
|
animated: false
|
||||||
|
}
|
||||||
|
Circle {
|
||||||
|
id: iconMask
|
||||||
|
visible: false
|
||||||
|
layer.enabled: true
|
||||||
|
diameter: appIcon.implicitSize
|
||||||
|
}
|
||||||
|
Loader {
|
||||||
|
id: renderedIconLoader
|
||||||
|
anchors.fill: appIcon
|
||||||
|
visible: root.activeWindow
|
||||||
|
sourceComponent: Colorizer {
|
||||||
|
implicitWidth: appIcon.implicitWidth
|
||||||
|
implicitHeight: appIcon.implicitHeight
|
||||||
|
colorizationColor: Appearance.m3colors.darkmode ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
|
||||||
|
colorization: Config.options.bar.workspaces.monochromeIcons ? 0.8 : 0.5
|
||||||
|
brightness: 0
|
||||||
|
source: appIcon
|
||||||
|
|
||||||
|
maskEnabled: true
|
||||||
|
maskSource: iconMask
|
||||||
|
maskThresholdMin: 0.5
|
||||||
|
maskSpreadAtMin: 1
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: !renderedIconLoader.visible
|
||||||
|
text: "overview_key"
|
||||||
|
iconSize: 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: !root.vertical
|
||||||
|
Layout.rightMargin: 4
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.fillHeight: true
|
||||||
|
// No overflow
|
||||||
|
Layout.maximumWidth: implicitWidth
|
||||||
|
Layout.fillWidth: true
|
||||||
|
// Size
|
||||||
|
implicitWidth: winText.implicitWidth
|
||||||
|
implicitHeight: winText.implicitHeight
|
||||||
|
|
||||||
|
FlyFadeEnterChoreographable {
|
||||||
|
anchors.fill: parent
|
||||||
|
progress: contentRoot.containsMouse ? 0 : 1
|
||||||
|
reverseDirection: true
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
VisuallyCenteredStyledText {
|
||||||
|
id: winText
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
// Styles & text
|
||||||
|
font.pixelSize: root.fontPixelSize
|
||||||
|
text: root.primaryText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FlyFadeEnterChoreographable {
|
||||||
|
anchors.fill: parent
|
||||||
|
progress: contentRoot.containsMouse ? 1 : 0
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
VisuallyCenteredStyledText {
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
// Styles & text
|
||||||
|
font.pixelSize: root.fontPixelSize
|
||||||
|
text: root.secondaryText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component WinOptionsPopup: ChoreographerLoader {
|
||||||
|
sourceComponent: ChoreographerGridLayout {
|
||||||
|
id: popupRoot
|
||||||
|
|
||||||
|
columns: 3
|
||||||
|
rowSpacing: 8
|
||||||
|
columnSpacing: 6
|
||||||
|
|
||||||
|
FlyFadeEnterChoreographable {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.columnSpan: 3
|
||||||
|
StyledText {
|
||||||
|
width: parent.width
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
text: root.hasFocusedWindow ? Translation.tr("Window options") : Translation.tr("Launch")
|
||||||
|
// font.pixelSize: Appearance.font.pixelSize.title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlyFadeEnterChoreographable {
|
||||||
|
visible: !root.hasFocusedWindow
|
||||||
|
PopupLabeledIconButton {
|
||||||
|
materialSymbol: "terminal"
|
||||||
|
text: Translation.tr("Terminal")
|
||||||
|
onClicked: Quickshell.execDetached(["bash", "-c", Config.options.apps.terminal]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlyFadeEnterChoreographable {
|
||||||
|
visible: !root.hasFocusedWindow
|
||||||
|
PopupLabeledIconButton {
|
||||||
|
materialSymbol: "files"
|
||||||
|
text: Translation.tr("Files")
|
||||||
|
onClicked: Qt.openUrlExternally(Directories.home);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlyFadeEnterChoreographable {
|
||||||
|
visible: !root.hasFocusedWindow
|
||||||
|
PopupLabeledIconButton {
|
||||||
|
materialSymbol: "language"
|
||||||
|
text: Translation.tr("Browser")
|
||||||
|
// Kinda hacky. Works with Google and DDG at least
|
||||||
|
onClicked: Qt.openUrlExternally(Config.options.search.engineBaseUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlyFadeEnterChoreographable {
|
||||||
|
visible: root.hasFocusedWindow
|
||||||
|
PopupLabeledIconButton {
|
||||||
|
materialSymbol: "content_copy"
|
||||||
|
text: Translation.tr("Address")
|
||||||
|
onClicked: Quickshell.clipboardText = root.activeHyprlandClient.address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlyFadeEnterChoreographable {
|
||||||
|
visible: root.hasFocusedWindow
|
||||||
|
PopupLabeledIconButton {
|
||||||
|
property bool toFloat: !(root.activeHyprlandClient?.floating ?? false)
|
||||||
|
materialSymbol: toFloat ? "picture_in_picture_center" : "side_navigation"
|
||||||
|
text: toFloat ? Translation.tr("Float") : Translation.tr("Tile")
|
||||||
|
onClicked: {
|
||||||
|
Hyprland.dispatch(`hl.dsp.window.float({action = "toggle", window = {address=${root.activeHyprlandClient.address}}})`)
|
||||||
|
HyprlandData.updateWindowList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlyFadeEnterChoreographable {
|
||||||
|
visible: root.hasFocusedWindow
|
||||||
|
PopupLabeledIconButton {
|
||||||
|
materialSymbol: "warning"
|
||||||
|
text: Translation.tr("Kill")
|
||||||
|
colBackground: Appearance.colors.colError
|
||||||
|
colForeground: Appearance.colors.colOnError
|
||||||
|
onClicked: {
|
||||||
|
Hyprland.dispatch(`hl.dsp.window.kill({window = {address=${root.activeHyprlandClient.address}}})`)
|
||||||
|
HyprlandData.updateWindowList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component PopupLabeledIconButton: Column {
|
||||||
|
id: licobtn
|
||||||
|
property string materialSymbol: "circle"
|
||||||
|
property string text: "Label"
|
||||||
|
property alias colBackground: btn.colBackground
|
||||||
|
property alias colForeground: btn.colForeground
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
signal clicked()
|
||||||
|
onClicked: root.showPopup = false
|
||||||
|
|
||||||
|
StyledIconButton {
|
||||||
|
id: btn
|
||||||
|
implicitWidth: 70
|
||||||
|
implicitHeight: 50
|
||||||
|
text: licobtn.materialSymbol
|
||||||
|
iconSize: 24
|
||||||
|
colBackground: Appearance.colors.colLayer4
|
||||||
|
colForeground: Appearance.colors.colOnLayer4
|
||||||
|
onClicked: licobtn.clicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
text: licobtn.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,441 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import qs
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.models
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.common.functions
|
||||||
|
import qs.services
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
HBarWidgetContainer {
|
||||||
|
id: containerRoot
|
||||||
|
ButtonMouseArea {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Layout.topMargin: (5 + 5 * containerRoot.startSide) * containerRoot.vertical
|
||||||
|
Layout.bottomMargin: (5 + 5 * containerRoot.endSide) * containerRoot.vertical
|
||||||
|
Layout.leftMargin: (3 + 4 * containerRoot.startSide) * !containerRoot.vertical
|
||||||
|
Layout.rightMargin: (3 + 4 * containerRoot.endSide) * !containerRoot.vertical
|
||||||
|
|
||||||
|
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen)
|
||||||
|
WorkspaceModel {
|
||||||
|
id: wsModel
|
||||||
|
monitor: root.monitor
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool vertical: Config.options.bar.vertical
|
||||||
|
property bool superPressAndHeld: false // Relevant modifications at bottom of file
|
||||||
|
|
||||||
|
property real workspaceButtonWidth: 26
|
||||||
|
property real activeWorkspaceMargin: 2
|
||||||
|
property real activeWorkspaceSize: workspaceButtonWidth - activeWorkspaceMargin * 2
|
||||||
|
property real workspaceIconSize: workspaceButtonWidth * 0.69
|
||||||
|
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
|
||||||
|
property real workspaceIconOpacityShrinked: 1
|
||||||
|
property real workspaceIconMarginShrinked: -4
|
||||||
|
property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % wsModel.shownCount
|
||||||
|
property real specialTextSize: workspaceButtonWidth * 0.5
|
||||||
|
|
||||||
|
Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: vertical
|
||||||
|
Layout.fillHeight: !vertical
|
||||||
|
readonly property real barThickness: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight
|
||||||
|
implicitWidth: vertical ? barThickness : occupiedIndicators.implicitWidth
|
||||||
|
implicitHeight: vertical ? occupiedIndicators.implicitHeight : barThickness
|
||||||
|
|
||||||
|
property real specialBlur: (wsModel.specialWorkspaceActive && !containsMouse) ? 1 : 0
|
||||||
|
Behavior on specialBlur {
|
||||||
|
animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interactions
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
hoverEnabled: true
|
||||||
|
property int hoverIndex: {
|
||||||
|
const position = root.vertical ? mouseY : mouseX;
|
||||||
|
return Math.floor(position / root.workspaceButtonWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchWorkspaceToHovered() {
|
||||||
|
Hyprland.dispatch(`hl.dsp.focus({workspace = ${wsModel.getWorkspaceIdAt(hoverIndex)}})`)
|
||||||
|
}
|
||||||
|
onPressed: (mouse) => {
|
||||||
|
if (mouse.button == Qt.LeftButton)
|
||||||
|
switchWorkspaceToHovered()
|
||||||
|
else if (mouse.button == Qt.RightButton)
|
||||||
|
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||||
|
}
|
||||||
|
onWheel: (event) => {
|
||||||
|
if (event.angleDelta.y < 0)
|
||||||
|
Hyprland.dispatch(`hl.dsp.focus({workspace = "r+1"})`);
|
||||||
|
else if (event.angleDelta.y > 0)
|
||||||
|
Hyprland.dispatch(`hl.dsp.focus({workspace = "r-1"})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indications
|
||||||
|
Item {
|
||||||
|
id: regularWorkspaces
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
scale: 1 - 0.08 * root.specialBlur
|
||||||
|
layer.smooth: true
|
||||||
|
layer.enabled: root.specialBlur > 0
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
brightness: -0.1 * root.specialBlur
|
||||||
|
blurEnabled: true
|
||||||
|
blur: root.specialBlur
|
||||||
|
blurMax: 32
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////// Occupied indicators ///////////////////
|
||||||
|
StyledRectangle {
|
||||||
|
id: occupiedIndicatorsBg
|
||||||
|
anchors.fill: parent
|
||||||
|
contentLayer: StyledRectangle.ContentLayer.Group
|
||||||
|
color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4)
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkspaceLayout {
|
||||||
|
id: occupiedIndicators
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: wsModel.shownCount
|
||||||
|
delegate: Item {
|
||||||
|
id: wsBg
|
||||||
|
required property int index
|
||||||
|
readonly property int wsId: wsModel.getWorkspaceIdAt(index)
|
||||||
|
property bool currentOccupied: wsModel.occupied[index] && wsId != wsModel.fakeWorkspace
|
||||||
|
property bool previousOccupied: index > 0 && wsModel.occupied[index - 1] && (wsId - 1) != wsModel.fakeWorkspace
|
||||||
|
property bool nextOccupied: index < wsModel.shownCount - 1 && wsModel.occupied[index + 1] && (wsId + 1) != wsModel.fakeWorkspace
|
||||||
|
implicitWidth: root.workspaceButtonWidth
|
||||||
|
implicitHeight: root.workspaceButtonWidth
|
||||||
|
|
||||||
|
// The idea: over-stretch to occupied sides, animate this for a smooth transition.
|
||||||
|
// masking already prevents weird overlaps
|
||||||
|
Pill {
|
||||||
|
property real undirectionalWidth: root.workspaceButtonWidth * wsBg.currentOccupied
|
||||||
|
property real undirectionalLength: root.workspaceButtonWidth * (1 + 0.5 * wsBg.previousOccupied + 0.5 * wsBg.nextOccupied) * currentOccupied
|
||||||
|
property real undirectionalOffset: (!wsBg.currentOccupied ? 0.5 : -0.5 * wsBg.previousOccupied) * root.workspaceButtonWidth
|
||||||
|
anchors.verticalCenter: root.vertical ? undefined : parent.verticalCenter
|
||||||
|
anchors.horizontalCenter: root.vertical ? parent.horizontalCenter : undefined
|
||||||
|
x: root.vertical ? 0 : undirectionalOffset
|
||||||
|
y: root.vertical ? undirectionalOffset : 0
|
||||||
|
implicitWidth: root.vertical ? undirectionalWidth : undirectionalLength
|
||||||
|
implicitHeight: root.vertical ? undirectionalLength : undirectionalWidth
|
||||||
|
|
||||||
|
Behavior on undirectionalWidth {
|
||||||
|
animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
Behavior on undirectionalLength {
|
||||||
|
animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
Behavior on undirectionalOffset {
|
||||||
|
animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaskMultiEffect {
|
||||||
|
id: occupiedIndicatorsMultiEffect
|
||||||
|
z: 1
|
||||||
|
anchors.centerIn: parent
|
||||||
|
implicitWidth: occupiedIndicators.implicitWidth
|
||||||
|
implicitHeight: occupiedIndicators.implicitHeight
|
||||||
|
source: occupiedIndicatorsBg
|
||||||
|
maskSource: occupiedIndicators
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////// Active indicator ///////////////////
|
||||||
|
TrailingIndicator {
|
||||||
|
id: activeIndicator
|
||||||
|
anchors.fill: parent
|
||||||
|
z: 2
|
||||||
|
|
||||||
|
index: root.workspaceIndexInGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////// Hover ///////////////////
|
||||||
|
TrailingIndicator {
|
||||||
|
id: interactionIndicator
|
||||||
|
z: 3
|
||||||
|
index: root.containsMouse ? root.hoverIndex : root.workspaceIndexInGroup
|
||||||
|
color: "transparent"
|
||||||
|
StateOverlay {
|
||||||
|
id: hoverOverlay
|
||||||
|
anchors.fill: interactionIndicator.indicatorRectangle
|
||||||
|
radius: root.activeWorkspaceSize / 2
|
||||||
|
hover: root.containsMouse
|
||||||
|
press: root.containsPress
|
||||||
|
drag: true // There are too many layers so we need to force this to be a lil more opaque
|
||||||
|
contentColor: Appearance.colors.colPrimary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////// Numbers ///////////////////
|
||||||
|
WorkspaceLayout {
|
||||||
|
id: numbersGrid
|
||||||
|
z: 4
|
||||||
|
layer.enabled: true // For the masking
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: wsModel.shownCount
|
||||||
|
delegate: NumberWorkspaceItem {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Colorizer {
|
||||||
|
z: 5
|
||||||
|
anchors.fill: numbersGrid
|
||||||
|
colorizationColor: Appearance.colors.colOnPrimary
|
||||||
|
sourceColor: Appearance.colors.colOnSecondaryContainer
|
||||||
|
|
||||||
|
source: activeIndicator
|
||||||
|
maskEnabled: true
|
||||||
|
maskSource: numbersGrid
|
||||||
|
|
||||||
|
maskThresholdMin: 0.5
|
||||||
|
maskSpreadAtMin: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////// App icons ///////////////////
|
||||||
|
WorkspaceLayout {
|
||||||
|
id: appsGrid
|
||||||
|
z: 6
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: wsModel.shownCount
|
||||||
|
delegate: WorkspaceItem {
|
||||||
|
id: wsApp
|
||||||
|
property var biggestWindow: wsModel.biggestWindow[index]
|
||||||
|
property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing")
|
||||||
|
|
||||||
|
AppIcon {
|
||||||
|
id: appIcon
|
||||||
|
property real cornerMargin: (!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons && wsApp.biggestWindow) ? (root.workspaceButtonWidth - root.workspaceIconSize) / 2 : root.workspaceIconMarginShrinked
|
||||||
|
anchors {
|
||||||
|
bottom: parent.bottom
|
||||||
|
right: parent.right
|
||||||
|
bottomMargin: (parent.implicitHeight - root.workspaceButtonWidth) / 2 + cornerMargin
|
||||||
|
rightMargin: (parent.implicitWidth - root.workspaceButtonWidth) / 2 + cornerMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
animated: !wsApp.biggestWindow // Prevent the "image-missing" icon
|
||||||
|
visible: false // Prevent dupe: the colorizer already copies the icon
|
||||||
|
|
||||||
|
source: wsApp.mainAppIconSource
|
||||||
|
implicitSize: NumberUtils.roundToEven(root.workspaceIconSize)
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
Behavior on cornerMargin {
|
||||||
|
animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Circle {
|
||||||
|
id: iconMask
|
||||||
|
visible: false
|
||||||
|
layer.enabled: true
|
||||||
|
diameter: appIcon.implicitSize
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader { // Somehow putting this multieffect in a loader prevents it from not showing up
|
||||||
|
id: colorizer
|
||||||
|
anchors.fill: appIcon
|
||||||
|
sourceComponent: Colorizer {
|
||||||
|
implicitWidth: appIcon.implicitWidth
|
||||||
|
implicitHeight: appIcon.implicitHeight
|
||||||
|
colorizationColor: Appearance.m3colors.darkmode ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
|
||||||
|
colorization: Config.options.bar.workspaces.monochromeIcons ? 0.8 : 0.5
|
||||||
|
brightness: 0
|
||||||
|
source: appIcon
|
||||||
|
|
||||||
|
opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 : (wsApp.biggestWindow && !root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? 1 : wsApp.biggestWindow ? root.workspaceIconOpacityShrinked : 0
|
||||||
|
visible: opacity > 0
|
||||||
|
scale: ((!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? root.workspaceIconSize : root.workspaceIconSizeShrinked) / root.workspaceIconSize
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
Behavior on scale {
|
||||||
|
animation: Appearance.animation.elementMoveSmall.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
maskEnabled: true
|
||||||
|
maskSource: iconMask
|
||||||
|
maskThresholdMin: 0.5
|
||||||
|
maskSpreadAtMin: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FadeLoader {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
shown: wsModel.specialWorkspaceActive
|
||||||
|
scale: 0.8 + 0.2 * root.specialBlur
|
||||||
|
|
||||||
|
opacity: root.specialBlur
|
||||||
|
Behavior on opacity {} // Don't animate, as specialBlur is already animated
|
||||||
|
|
||||||
|
sourceComponent: Pill {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
property real undirectionalWidth: root.activeWorkspaceSize
|
||||||
|
property real undirectionalLength: {
|
||||||
|
const base = root.workspaceButtonWidth * Math.min(1.35, wsModel.shownCount); // Who tf only configures only 2 workspaces shown anyway?
|
||||||
|
if (root.vertical)
|
||||||
|
return base;
|
||||||
|
return specialWsText.implicitWidth + undirectionalWidth;
|
||||||
|
}
|
||||||
|
color: Appearance.colors.colPrimary
|
||||||
|
|
||||||
|
implicitWidth: root.vertical ? undirectionalWidth : undirectionalLength
|
||||||
|
implicitHeight: root.vertical ? undirectionalLength : undirectionalWidth
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: specialWsText
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: (!root.vertical ? wsModel.specialWorkspaceName : "S")
|
||||||
|
color: Appearance.colors.colOnPrimary
|
||||||
|
font.pixelSize: root.specialTextSize
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on undirectionalLength {
|
||||||
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////// Super key press handling ///////////////////
|
||||||
|
Timer {
|
||||||
|
id: superPressAndHeldTimer
|
||||||
|
interval: (Config?.options.bar.autoHide.showWhenPressingSuper.delay ?? 100)
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
root.superPressAndHeld = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: GlobalStates
|
||||||
|
function onSuperDownChanged() {
|
||||||
|
if (!Config?.options.bar.autoHide.showWhenPressingSuper.enable)
|
||||||
|
return;
|
||||||
|
if (GlobalStates.superDown)
|
||||||
|
superPressAndHeldTimer.restart();
|
||||||
|
else {
|
||||||
|
superPressAndHeldTimer.stop();
|
||||||
|
root.superPressAndHeld = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onSuperReleaseMightTriggerChanged() {
|
||||||
|
superPressAndHeldTimer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////// Components ///////////////////
|
||||||
|
component WorkspaceLayout: Box {
|
||||||
|
anchors {
|
||||||
|
top: !root.vertical ? parent.top : undefined
|
||||||
|
bottom: !root.vertical ? parent.bottom : undefined
|
||||||
|
left: root.vertical ? parent.left : undefined
|
||||||
|
right: root.vertical ? parent.right : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
rowSpacing: 0
|
||||||
|
columnSpacing: 0
|
||||||
|
vertical: root.vertical
|
||||||
|
}
|
||||||
|
|
||||||
|
component WorkspaceItem: Item {
|
||||||
|
required property int index
|
||||||
|
readonly property int wsId: wsModel.getWorkspaceIdAt(index)
|
||||||
|
implicitWidth: root.vertical ? root.barThickness : root.workspaceButtonWidth
|
||||||
|
implicitHeight: root.vertical ? root.workspaceButtonWidth : root.barThickness
|
||||||
|
}
|
||||||
|
|
||||||
|
component NumberWorkspaceItem: WorkspaceItem {
|
||||||
|
id: wsNum
|
||||||
|
property bool hasBiggestWindow: !!wsModel.biggestWindow[index]
|
||||||
|
property int wsId: wsModel.getWorkspaceIdAt(index)
|
||||||
|
property color contentColor: (wsModel.occupied[wsNum.index] && wsId !== wsModel.fakeWorkspace) ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1Inactive
|
||||||
|
property bool showingNumbers: {
|
||||||
|
if (root.superPressAndHeld) return true;
|
||||||
|
if (GlobalStates.screenLocked) return false;
|
||||||
|
if (Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !wsNum.hasBiggestWindow)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FadeLoader {
|
||||||
|
shown: !wsNum.showingNumbers
|
||||||
|
anchors.centerIn: parent
|
||||||
|
Circle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
diameter: root.workspaceButtonWidth * 0.18
|
||||||
|
color: wsNum.contentColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FadeLoader {
|
||||||
|
shown: wsNum.showingNumbers
|
||||||
|
anchors.centerIn: parent
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
font {
|
||||||
|
pixelSize: Appearance.font.pixelSize.small - ((text.length - 1) * (text !== "10") * 2)
|
||||||
|
family: Config.options?.bar.workspaces.useNerdFont ? Appearance.font.family.iconNerd : defaultFont
|
||||||
|
}
|
||||||
|
color: wsNum.contentColor
|
||||||
|
text: Config.options?.bar.workspaces.numberMap[wsNum.wsId - 1] || wsNum.wsId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component TrailingIndicator: Item {
|
||||||
|
id: trailingIndicator
|
||||||
|
anchors.fill: parent
|
||||||
|
required property int index
|
||||||
|
property alias indicatorRectangle: indicatorRect
|
||||||
|
property alias color: indicatorRect.color
|
||||||
|
|
||||||
|
property var indexPair: AnimatedTabIndexPair {
|
||||||
|
id: idxPair
|
||||||
|
index: trailingIndicator.index
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRectangle {
|
||||||
|
id: indicatorRect
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: root.vertical ? undefined : parent.verticalCenter
|
||||||
|
horizontalCenter: root.vertical ? parent.horizontalCenter : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin
|
||||||
|
property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceSize
|
||||||
|
property real indicatorThickness: root.activeWorkspaceSize
|
||||||
|
|
||||||
|
contentLayer: StyledRectangle.ContentLayer.Group
|
||||||
|
radius: indicatorThickness / 2
|
||||||
|
color: Appearance.colors.colPrimary
|
||||||
|
|
||||||
|
x: root.vertical ? null : indicatorPosition
|
||||||
|
y: root.vertical ? indicatorPosition : null
|
||||||
|
implicitWidth: root.vertical ? indicatorThickness : indicatorLength
|
||||||
|
implicitHeight: root.vertical ? indicatorLength : indicatorThickness
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.modules.ii.bar as IIBar
|
||||||
|
import qs.modules.common as C
|
||||||
|
|
||||||
|
IIBar.Workspaces {
|
||||||
|
id: root
|
||||||
|
vertical: C.Config.options.bar.vertical
|
||||||
|
Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: vertical
|
||||||
|
Layout.fillHeight: !vertical
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: false
|
||||||
|
Layout.fillWidth: false
|
||||||
|
Layout.fillHeight: false
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: false
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
@@ -12,9 +12,8 @@ Item {
|
|||||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen)
|
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen)
|
||||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||||
|
|
||||||
property string activeWindowAddress: `0x${activeWindow?.HyprlandToplevel?.address}`
|
readonly property bool focusingThisMonitor: HyprlandData.activeWorkspace?.monitor == monitor?.name
|
||||||
property bool focusingThisMonitor: HyprlandData.activeWorkspace?.monitor == monitor?.name
|
readonly property var biggestWindow: HyprlandData.biggestWindowForWorkspace(HyprlandData.monitors[root.monitor?.id]?.activeWorkspace.id)
|
||||||
property var biggestWindow: HyprlandData.biggestWindowForWorkspace(HyprlandData.monitors[root.monitor?.id]?.activeWorkspace.id)
|
|
||||||
|
|
||||||
implicitWidth: colLayout.implicitWidth
|
implicitWidth: colLayout.implicitWidth
|
||||||
|
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ Item { // Bar content region
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
text: Network.materialSymbol
|
text: Icons.getNetworkMaterialSymbol()
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
iconSize: Appearance.font.pixelSize.larger
|
||||||
color: rightSidebarButton.colText
|
color: rightSidebarButton.colText
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
@@ -23,9 +24,9 @@ Item {
|
|||||||
radius: Appearance.rounding.small
|
radius: Appearance.rounding.small
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
BoxLayout {
|
||||||
id: gridLayout
|
id: gridLayout
|
||||||
columns: root.vertical ? 1 : -1
|
vertical: root.vertical
|
||||||
anchors {
|
anchors {
|
||||||
verticalCenter: root.vertical ? undefined : parent.verticalCenter
|
verticalCenter: root.vertical ? undefined : parent.verticalCenter
|
||||||
horizontalCenter: root.vertical ? parent.horizontalCenter : undefined
|
horizontalCenter: root.vertical ? parent.horizontalCenter : undefined
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
|
import qs.modules.common.functions
|
||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
import qs.services
|
import qs.services
|
||||||
import QtQuick
|
import QtQuick
|
||||||
@@ -14,7 +15,7 @@ StyledPopup {
|
|||||||
|
|
||||||
// Header
|
// Header
|
||||||
StyledPopupHeaderRow {
|
StyledPopupHeaderRow {
|
||||||
icon: "battery_android_full"
|
icon: Icons.getBatteryIcon(Battery.percentage * 100)
|
||||||
label: Translation.tr("Battery")
|
label: Translation.tr("Battery")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,18 +28,10 @@ StyledPopup {
|
|||||||
icon: "schedule"
|
icon: "schedule"
|
||||||
label: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:")
|
label: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:")
|
||||||
value: {
|
value: {
|
||||||
function formatTime(seconds) {
|
|
||||||
var h = Math.floor(seconds / 3600);
|
|
||||||
var m = Math.floor((seconds % 3600) / 60);
|
|
||||||
if (h > 0)
|
|
||||||
return `${h}h, ${m}m`;
|
|
||||||
else
|
|
||||||
return `${m}m`;
|
|
||||||
}
|
|
||||||
if (Battery.isCharging)
|
if (Battery.isCharging)
|
||||||
return formatTime(Battery.timeToFull);
|
return DateUtils.formatDuration(Battery.timeToFull);
|
||||||
else
|
else
|
||||||
return formatTime(Battery.timeToEmpty);
|
return DateUtils.formatDuration(Battery.timeToEmpty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,13 +47,7 @@ StyledPopup {
|
|||||||
return Translation.tr("Discharging:");
|
return Translation.tr("Discharging:");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value: {
|
value: `${Battery.energyRate.toFixed(2)}W`
|
||||||
if (Battery.chargeState == 4) {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
return `${Battery.energyRate.toFixed(2)}W`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledPopupValueRow {
|
StyledPopupValueRow {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import QtQuick.Layouts
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
property bool borderless: Config.options.bar.borderless
|
|
||||||
property bool showDate: Config.options.bar.verbose
|
property bool showDate: Config.options.bar.verbose
|
||||||
implicitWidth: rowLayout.implicitWidth
|
implicitWidth: rowLayout.implicitWidth
|
||||||
implicitHeight: Appearance.sizes.barHeight
|
implicitHeight: Appearance.sizes.barHeight
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ RippleButton {
|
|||||||
CustomIcon {
|
CustomIcon {
|
||||||
id: distroIcon
|
id: distroIcon
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: 19.5
|
width: 20
|
||||||
height: 19.5
|
height: 20
|
||||||
source: Config.options.bar.topLeftIcon == 'distro' ? SystemInfo.distroIcon : `${Config.options.bar.topLeftIcon}-symbolic`
|
source: Config.options.bar.topLeftIcon == 'distro' ? SystemInfo.distroIcon : `${Config.options.bar.topLeftIcon}-symbolic`
|
||||||
colorize: true
|
colorize: true
|
||||||
color: Appearance.colors.colOnLayer0
|
color: Appearance.colors.colOnLayer0
|
||||||
|
|||||||
@@ -7,11 +7,6 @@ import QtQuick.Layouts
|
|||||||
StyledPopup {
|
StyledPopup {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
// Helper function to format KB to GB
|
|
||||||
function formatKB(kb) {
|
|
||||||
return (kb / (1024 * 1024)).toFixed(1) + " GB";
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 12
|
spacing: 12
|
||||||
@@ -29,17 +24,17 @@ StyledPopup {
|
|||||||
StyledPopupValueRow {
|
StyledPopupValueRow {
|
||||||
icon: "clock_loader_60"
|
icon: "clock_loader_60"
|
||||||
label: Translation.tr("Used:")
|
label: Translation.tr("Used:")
|
||||||
value: root.formatKB(ResourceUsage.memoryUsed)
|
value: ResourceUsage.kbToGbString(ResourceUsage.memoryUsed)
|
||||||
}
|
}
|
||||||
StyledPopupValueRow {
|
StyledPopupValueRow {
|
||||||
icon: "check_circle"
|
icon: "check_circle"
|
||||||
label: Translation.tr("Free:")
|
label: Translation.tr("Free:")
|
||||||
value: root.formatKB(ResourceUsage.memoryFree)
|
value: ResourceUsage.kbToGbString(ResourceUsage.memoryFree)
|
||||||
}
|
}
|
||||||
StyledPopupValueRow {
|
StyledPopupValueRow {
|
||||||
icon: "empty_dashboard"
|
icon: "empty_dashboard"
|
||||||
label: Translation.tr("Total:")
|
label: Translation.tr("Total:")
|
||||||
value: root.formatKB(ResourceUsage.memoryTotal)
|
value: ResourceUsage.kbToGbString(ResourceUsage.memoryTotal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,17 +53,17 @@ StyledPopup {
|
|||||||
StyledPopupValueRow {
|
StyledPopupValueRow {
|
||||||
icon: "clock_loader_60"
|
icon: "clock_loader_60"
|
||||||
label: Translation.tr("Used:")
|
label: Translation.tr("Used:")
|
||||||
value: root.formatKB(ResourceUsage.swapUsed)
|
value: ResourceUsage.kbToGbString(ResourceUsage.swapUsed)
|
||||||
}
|
}
|
||||||
StyledPopupValueRow {
|
StyledPopupValueRow {
|
||||||
icon: "check_circle"
|
icon: "check_circle"
|
||||||
label: Translation.tr("Free:")
|
label: Translation.tr("Free:")
|
||||||
value: root.formatKB(ResourceUsage.swapFree)
|
value: ResourceUsage.kbToGbString(ResourceUsage.swapFree)
|
||||||
}
|
}
|
||||||
StyledPopupValueRow {
|
StyledPopupValueRow {
|
||||||
icon: "empty_dashboard"
|
icon: "empty_dashboard"
|
||||||
label: Translation.tr("Total:")
|
label: Translation.tr("Total:")
|
||||||
value: root.formatKB(ResourceUsage.swapTotal)
|
value: ResourceUsage.kbToGbString(ResourceUsage.swapTotal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
BoxLayout {
|
||||||
id: gridLayout
|
id: gridLayout
|
||||||
columns: root.vertical ? 1 : -1
|
vertical: root.vertical
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
rowSpacing: 8
|
rowSpacing: 8
|
||||||
columnSpacing: 15
|
columnSpacing: 15
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import qs.modules.common
|
|||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
import qs.modules.common.functions
|
import qs.modules.common.functions
|
||||||
|
|
||||||
MouseArea {
|
ButtonMouseArea {
|
||||||
id: root
|
id: root
|
||||||
required property SystemTrayItem item
|
required property SystemTrayItem item
|
||||||
property bool targetMenuOpen: false
|
property bool targetMenuOpen: false
|
||||||
@@ -19,8 +19,10 @@ MouseArea {
|
|||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
implicitWidth: 20
|
property real iconSize: 20
|
||||||
implicitHeight: 20
|
property real backgroundSize: 26
|
||||||
|
implicitWidth: iconSize
|
||||||
|
implicitHeight: iconSize
|
||||||
onPressed: (event) => {
|
onPressed: (event) => {
|
||||||
switch (event.button) {
|
switch (event.button) {
|
||||||
case Qt.LeftButton:
|
case Qt.LeftButton:
|
||||||
@@ -40,6 +42,16 @@ MouseArea {
|
|||||||
tooltip.text = TrayService.getTooltipForItem(root.item);
|
tooltip.text = TrayService.getTooltipForItem(root.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateOverlay {
|
||||||
|
id: hoverOverlay
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: root.backgroundSize
|
||||||
|
height: root.backgroundSize
|
||||||
|
radius: root.backgroundSize / 2
|
||||||
|
hover: root.containsMouse
|
||||||
|
press: root.containsPress
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: menu
|
id: menu
|
||||||
function open() {
|
function open() {
|
||||||
@@ -73,8 +85,8 @@ MouseArea {
|
|||||||
visible: !Config.options.tray.monochromeIcons
|
visible: !Config.options.tray.monochromeIcons
|
||||||
source: root.item.icon
|
source: root.item.icon
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.width
|
width: root.iconSize
|
||||||
height: parent.height
|
height: root.iconSize
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
|
|||||||
@@ -187,7 +187,10 @@ PopupWindow {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
visible: root.trayItemId !== undefined && root.trayItemId.length > 0 && stackView.depth === 1
|
visible: root.trayItemId !== undefined && root.trayItemId.length > 0 && stackView.depth === 1
|
||||||
releaseAction: () => TrayService.togglePin(root.trayItemId);
|
releaseAction: () => {
|
||||||
|
GlobalFocusGrab.dismiss();
|
||||||
|
TrayService.togglePin(root.trayItemId);
|
||||||
|
}
|
||||||
|
|
||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
anchors {
|
anchors {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
import qs
|
import qs
|
||||||
import qs.services
|
import qs.services
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
@@ -15,22 +16,22 @@ import Qt5Compat.GraphicalEffects
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
property bool vertical: false
|
property bool vertical: false
|
||||||
property bool borderless: Config.options.bar.borderless
|
|
||||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen)
|
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen)
|
||||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||||
readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1
|
readonly property bool activeActuallyFocused: activeWindow?.activated ?? false
|
||||||
|
|
||||||
|
WorkspaceModel {
|
||||||
|
id: wsModel
|
||||||
|
monitor: root.monitor
|
||||||
|
}
|
||||||
|
|
||||||
readonly property int workspacesShown: Config.options.bar.workspaces.shown
|
|
||||||
readonly property int workspaceGroup: Math.floor((effectiveActiveWorkspaceId - 1) / root.workspacesShown)
|
|
||||||
property list<bool> workspaceOccupied: []
|
|
||||||
property int widgetPadding: 4
|
|
||||||
property int workspaceButtonWidth: 26
|
property int workspaceButtonWidth: 26
|
||||||
property real activeWorkspaceMargin: 2
|
property real activeWorkspaceMargin: 2
|
||||||
property real workspaceIconSize: workspaceButtonWidth * 0.69
|
property real workspaceIconSize: workspaceButtonWidth * 0.69
|
||||||
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
|
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
|
||||||
property real workspaceIconOpacityShrinked: 1
|
property real workspaceIconOpacityShrinked: 1
|
||||||
property real workspaceIconMarginShrinked: -4
|
property real workspaceIconMarginShrinked: -4
|
||||||
property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown
|
property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % wsModel.shownCount
|
||||||
|
|
||||||
property bool showNumbers: false
|
property bool showNumbers: false
|
||||||
Timer {
|
Timer {
|
||||||
@@ -56,33 +57,8 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to update workspaceOccupied
|
implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : (root.workspaceButtonWidth * wsModel.shownCount)
|
||||||
function updateWorkspaceOccupied() {
|
implicitHeight: root.vertical ? (root.workspaceButtonWidth * wsModel.shownCount) : Appearance.sizes.barHeight
|
||||||
workspaceOccupied = Array.from({ length: root.workspacesShown }, (_, i) => {
|
|
||||||
return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * root.workspacesShown + i + 1);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Occupied workspace updates
|
|
||||||
Component.onCompleted: updateWorkspaceOccupied()
|
|
||||||
Connections {
|
|
||||||
target: Hyprland.workspaces
|
|
||||||
function onValuesChanged() {
|
|
||||||
updateWorkspaceOccupied();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Connections {
|
|
||||||
target: Hyprland
|
|
||||||
function onFocusedWorkspaceChanged() {
|
|
||||||
updateWorkspaceOccupied();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onWorkspaceGroupChanged: {
|
|
||||||
updateWorkspaceOccupied();
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : (root.workspaceButtonWidth * root.workspacesShown)
|
|
||||||
implicitHeight: root.vertical ? (root.workspaceButtonWidth * root.workspacesShown) : Appearance.sizes.barHeight
|
|
||||||
|
|
||||||
// Scroll to switch workspaces
|
// Scroll to switch workspaces
|
||||||
WheelHandler {
|
WheelHandler {
|
||||||
@@ -106,25 +82,27 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Workspaces - background
|
// Workspaces - background
|
||||||
Grid {
|
Box {
|
||||||
z: 1
|
z: 1
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
rowSpacing: 0
|
rowSpacing: 0
|
||||||
columnSpacing: 0
|
columnSpacing: 0
|
||||||
columns: root.vertical ? 1 : root.workspacesShown
|
vertical: root.vertical
|
||||||
rows: root.vertical ? root.workspacesShown : 1
|
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.workspacesShown
|
model: wsModel.shownCount
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property int index
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
z: 1
|
z: 1
|
||||||
implicitWidth: workspaceButtonWidth
|
implicitWidth: root.workspaceButtonWidth
|
||||||
implicitHeight: workspaceButtonWidth
|
implicitHeight: root.workspaceButtonWidth
|
||||||
radius: (width / 2)
|
radius: (width / 2)
|
||||||
property var previousOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && root.effectiveActiveWorkspaceId === index))
|
property bool thisOccupied: (wsModel.occupied[index] && !(!wsModel.currentWorkspaceNotFake && monitor?.activeWorkspace?.id === index+1))
|
||||||
property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && root.effectiveActiveWorkspaceId === index+2))
|
property var previousOccupied: (wsModel.occupied[index-1] && !(!wsModel.currentWorkspaceNotFake && monitor?.activeWorkspace?.id === index))
|
||||||
|
property var rightOccupied: (wsModel.occupied[index+1] && !(!wsModel.currentWorkspaceNotFake && monitor?.activeWorkspace?.id === index+2))
|
||||||
property var radiusPrev: previousOccupied ? 0 : (width / 2)
|
property var radiusPrev: previousOccupied ? 0 : (width / 2)
|
||||||
property var radiusNext: rightOccupied ? 0 : (width / 2)
|
property var radiusNext: rightOccupied ? 0 : (width / 2)
|
||||||
|
|
||||||
@@ -134,7 +112,7 @@ Item {
|
|||||||
bottomRightRadius: radiusNext
|
bottomRightRadius: radiusNext
|
||||||
|
|
||||||
color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4)
|
color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4)
|
||||||
opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && root.effectiveActiveWorkspaceId === index+1)) ? 1 : 0
|
opacity: thisOccupied ? 1 : 0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
@@ -169,9 +147,9 @@ Item {
|
|||||||
id: idxPair
|
id: idxPair
|
||||||
index: root.workspaceIndexInGroup
|
index: root.workspaceIndexInGroup
|
||||||
}
|
}
|
||||||
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * workspaceButtonWidth + root.activeWorkspaceMargin
|
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin
|
||||||
property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * workspaceButtonWidth + workspaceButtonWidth - root.activeWorkspaceMargin * 2
|
property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * root.workspaceButtonWidth + root.workspaceButtonWidth - root.activeWorkspaceMargin * 2
|
||||||
property real indicatorThickness: workspaceButtonWidth - root.activeWorkspaceMargin * 2
|
property real indicatorThickness: root.workspaceButtonWidth - root.activeWorkspaceMargin * 2
|
||||||
|
|
||||||
x: root.vertical ? null : indicatorPosition
|
x: root.vertical ? null : indicatorPosition
|
||||||
implicitWidth: root.vertical ? indicatorThickness : indicatorLength
|
implicitWidth: root.vertical ? indicatorThickness : indicatorLength
|
||||||
@@ -181,22 +159,21 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Workspaces - numbers
|
// Workspaces - numbers
|
||||||
Grid {
|
Box {
|
||||||
|
id: wsNumbers
|
||||||
z: 3
|
z: 3
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
columns: root.vertical ? 1 : root.workspacesShown
|
vertical: root.vertical
|
||||||
rows: root.vertical ? root.workspacesShown : 1
|
|
||||||
columnSpacing: 0
|
columnSpacing: 0
|
||||||
rowSpacing: 0
|
rowSpacing: 0
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.workspacesShown
|
model: wsModel.shownCount
|
||||||
|
delegate: Button {
|
||||||
Button {
|
|
||||||
id: button
|
id: button
|
||||||
property int workspaceValue: workspaceGroup * root.workspacesShown + index + 1
|
required property int index
|
||||||
|
property int workspaceValue: wsModel.getWorkspaceIdAt(index)
|
||||||
implicitHeight: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight
|
implicitHeight: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight
|
||||||
implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.verticalBarWidth
|
implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.verticalBarWidth
|
||||||
onPressed: Hyprland.dispatch(`hl.dsp.focus({ workspace = ${workspaceValue}})`)
|
onPressed: Hyprland.dispatch(`hl.dsp.focus({ workspace = ${workspaceValue}})`)
|
||||||
@@ -205,11 +182,16 @@ Item {
|
|||||||
|
|
||||||
background: Item {
|
background: Item {
|
||||||
id: workspaceButtonBackground
|
id: workspaceButtonBackground
|
||||||
implicitWidth: workspaceButtonWidth
|
implicitWidth: root.workspaceButtonWidth
|
||||||
implicitHeight: workspaceButtonWidth
|
implicitHeight: root.workspaceButtonWidth
|
||||||
property var biggestWindow: HyprlandData.biggestWindowForWorkspace(button.workspaceValue)
|
property var biggestWindow: HyprlandData.biggestWindowForWorkspace(button.workspaceValue)
|
||||||
property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing")
|
property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing")
|
||||||
|
|
||||||
|
property color numberColor: (monitor?.activeWorkspace?.id == button.workspaceValue) ?
|
||||||
|
Appearance.m3colors.m3onPrimary :
|
||||||
|
(wsModel.occupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
|
||||||
|
Appearance.colors.colOnLayer1Inactive)
|
||||||
|
|
||||||
StyledText { // Workspace number text
|
StyledText { // Workspace number text
|
||||||
opacity: root.showNumbers
|
opacity: root.showNumbers
|
||||||
|| ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !workspaceButtonBackground.biggestWindow || root.showNumbers))
|
|| ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !workspaceButtonBackground.biggestWindow || root.showNumbers))
|
||||||
@@ -226,10 +208,7 @@ Item {
|
|||||||
}
|
}
|
||||||
text: Config.options?.bar.workspaces.numberMap[button.workspaceValue - 1] || button.workspaceValue
|
text: Config.options?.bar.workspaces.numberMap[button.workspaceValue - 1] || button.workspaceValue
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
color: (root.effectiveActiveWorkspaceId == button.workspaceValue) ?
|
color: workspaceButtonBackground.numberColor
|
||||||
Appearance.m3colors.m3onPrimary :
|
|
||||||
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
|
|
||||||
Appearance.colors.colOnLayer1Inactive)
|
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
@@ -243,13 +222,10 @@ Item {
|
|||||||
) ? 0 : 1
|
) ? 0 : 1
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: workspaceButtonWidth * 0.18
|
width: root.workspaceButtonWidth * 0.18
|
||||||
height: width
|
height: width
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
color: (root.effectiveActiveWorkspaceId == button.workspaceValue) ?
|
color: workspaceButtonBackground.numberColor
|
||||||
Appearance.m3colors.m3onPrimary :
|
|
||||||
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
|
|
||||||
Appearance.colors.colOnLayer1Inactive)
|
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
@@ -257,8 +233,8 @@ Item {
|
|||||||
}
|
}
|
||||||
Item { // Main app icon
|
Item { // Main app icon
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: workspaceButtonWidth
|
width: root.workspaceButtonWidth
|
||||||
height: workspaceButtonWidth
|
height: root.workspaceButtonWidth
|
||||||
opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 :
|
opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 :
|
||||||
(workspaceButtonBackground.biggestWindow && !root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
(workspaceButtonBackground.biggestWindow && !root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||||
1 : workspaceButtonBackground.biggestWindow ? workspaceIconOpacityShrinked : 0
|
1 : workspaceButtonBackground.biggestWindow ? workspaceIconOpacityShrinked : 0
|
||||||
@@ -268,9 +244,9 @@ Item {
|
|||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.bottomMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
anchors.bottomMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||||
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
|
(root.workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
|
||||||
anchors.rightMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
anchors.rightMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||||
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
|
(root.workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
|
||||||
|
|
||||||
source: workspaceButtonBackground.mainAppIconSource
|
source: workspaceButtonBackground.mainAppIconSource
|
||||||
implicitSize: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? workspaceIconSize : workspaceIconSizeShrinked
|
implicitSize: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? workspaceIconSize : workspaceIconSizeShrinked
|
||||||
@@ -309,12 +285,8 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ MouseArea {
|
|||||||
|
|
||||||
IconAndTextPair {
|
IconAndTextPair {
|
||||||
visible: Battery.available
|
visible: Battery.available
|
||||||
icon: Battery.isCharging ? "bolt" : "battery_android_full"
|
icon: Battery.isCharging ? "bolt" : Icons.getBatteryIcon(Battery.percentage * 100)
|
||||||
text: Math.round(Battery.percentage * 100)
|
text: Math.round(Battery.percentage * 100)
|
||||||
color: (Battery.isLow && !Battery.isCharging) ? Appearance.colors.colError : Appearance.colors.colOnSurfaceVariant
|
color: (Battery.isLow && !Battery.isCharging) ? Appearance.colors.colError : Appearance.colors.colOnSurfaceVariant
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ Rectangle {
|
|||||||
id: boltIcon
|
id: boltIcon
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
fill: 1
|
fill: 1
|
||||||
text: Battery.isCharging ? "bolt" : "battery_android_full"
|
text: Battery.isCharging ? "bolt" : Icons.getBatteryIcon(Battery.percentage * 100)
|
||||||
color: batteryWidget.colText
|
color: batteryWidget.colText
|
||||||
iconSize: 24
|
iconSize: 24
|
||||||
animateChange: true
|
animateChange: true
|
||||||
|
|||||||
@@ -95,6 +95,25 @@ Item { // Wrapper
|
|||||||
root.focusFirstItem();
|
root.focusFirstItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ctrl+N / Ctrl+P navigation
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (event.key === Qt.Key_N) {
|
||||||
|
if (appResults.visible && appResults.count > 0) {
|
||||||
|
// Wrap around the list rather than the default arrow key behaviour
|
||||||
|
appResults.currentIndex = (appResults.currentIndex + 1) % appResults.count;
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (event.key === Qt.Key_P) {
|
||||||
|
if (appResults.visible && appResults.count > 0) {
|
||||||
|
// Wrap around too
|
||||||
|
appResults.currentIndex = (appResults.count + appResults.currentIndex - 1) % appResults.count;
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledRectangularShadow {
|
StyledRectangularShadow {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ Scope {
|
|||||||
margins: Appearance.sizes.hyprlandGapsOut
|
margins: Appearance.sizes.hyprlandGapsOut
|
||||||
leftMargin: Appearance.sizes.elevationMargin
|
leftMargin: Appearance.sizes.elevationMargin
|
||||||
}
|
}
|
||||||
|
asynchronous: true
|
||||||
width: sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
|
width: sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
|
||||||
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ Item {
|
|||||||
return `${minutes}:${seconds}`;
|
return `${minutes}:${seconds}`;
|
||||||
}
|
}
|
||||||
font.pixelSize: 40
|
font.pixelSize: 40
|
||||||
|
font.features: { "tnum": 1 }
|
||||||
color: Appearance.m3colors.m3onSurface
|
color: Appearance.m3colors.m3onSurface
|
||||||
}
|
}
|
||||||
StyledText {
|
StyledText {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ Item {
|
|||||||
StyledText {
|
StyledText {
|
||||||
// Layout.preferredWidth: elapsedIndicator.width * 0.6 // Prevent shakiness
|
// Layout.preferredWidth: elapsedIndicator.width * 0.6 // Prevent shakiness
|
||||||
font.pixelSize: 40
|
font.pixelSize: 40
|
||||||
|
font.features: { "tnum": 1 }
|
||||||
color: Appearance.m3colors.m3onSurface
|
color: Appearance.m3colors.m3onSurface
|
||||||
text: {
|
text: {
|
||||||
let totalSeconds = Math.floor(TimerService.stopwatchTime) / 100
|
let totalSeconds = Math.floor(TimerService.stopwatchTime) / 100
|
||||||
@@ -64,9 +65,10 @@ Item {
|
|||||||
StyledText {
|
StyledText {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
font.pixelSize: 40
|
font.pixelSize: 40
|
||||||
|
font.features: { "tnum": 1 }
|
||||||
color: Appearance.colors.colSubtext
|
color: Appearance.colors.colSubtext
|
||||||
text: {
|
text: {
|
||||||
return `:<sub>${(Math.floor(TimerService.stopwatchTime) % 100).toString().padStart(2, '0')}</sub>`
|
return `:${(Math.floor(TimerService.stopwatchTime) % 100).toString().padStart(2, '0')}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@ import Quickshell.Hyprland
|
|||||||
|
|
||||||
QuickToggleButton {
|
QuickToggleButton {
|
||||||
toggled: Network.wifiStatus !== "disabled"
|
toggled: Network.wifiStatus !== "disabled"
|
||||||
buttonIcon: Network.materialSymbol
|
buttonIcon: Icons.getNetworkMaterialSymbol()
|
||||||
onClicked: Network.toggleWifi()
|
onClicked: Network.toggleWifi()
|
||||||
altAction: () => {
|
altAction: () => {
|
||||||
Quickshell.execDetached(["bash", "-c", `${Network.ethernet ? Config.options.apps.networkEthernet : Config.options.apps.network}`])
|
Quickshell.execDetached(["bash", "-c", `${Network.ethernet ? Config.options.apps.networkEthernet : Config.options.apps.network}`])
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ Item { // Bar content region
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
text: Network.materialSymbol
|
text: Icons.getNetworkMaterialSymbol()
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
iconSize: Appearance.font.pixelSize.larger
|
||||||
color: rightSidebarButton.colText
|
color: rightSidebarButton.colText
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import org.kde.kirigami as Kirigami
|
|
||||||
import qs.services
|
import qs.services
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
|
||||||
Kirigami.Icon {
|
W.AppIcon {
|
||||||
id: root
|
id: root
|
||||||
required property string iconName
|
required property string iconName
|
||||||
property bool separateLightDark: false
|
property bool separateLightDark: false
|
||||||
property bool tryCustomIcon: true
|
property bool tryCustomIcon: true
|
||||||
|
|
||||||
property real implicitSize: 26
|
|
||||||
implicitWidth: implicitSize
|
|
||||||
implicitHeight: implicitSize
|
|
||||||
|
|
||||||
animated: true
|
|
||||||
roundToIconSize: false
|
roundToIconSize: false
|
||||||
fallback: root.iconName
|
fallback: root.iconName
|
||||||
source: tryCustomIcon ? `${Looks.iconsPath}/${root.iconName}${!root.separateLightDark ? "" : Looks.dark ? "-dark" : "-light"}.svg` : fallback
|
source: tryCustomIcon ? `${Looks.iconsPath}/${root.iconName}${!root.separateLightDark ? "" : Looks.dark ? "-dark" : "-light"}.svg` : fallback
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ Singleton {
|
|||||||
property string internetIcon: {
|
property string internetIcon: {
|
||||||
if (Network.ethernet)
|
if (Network.ethernet)
|
||||||
return "ethernet";
|
return "ethernet";
|
||||||
if (Network.wifiEnabled) {
|
if (Network.wifiEnabled && Network.wifiStatus === "connected") {
|
||||||
const strength = Network.networkStrength;
|
const strength = Network.networkStrength;
|
||||||
return wifiIconForStrength(strength);
|
return root.wifiIconForStrength(strength);
|
||||||
}
|
}
|
||||||
if (Network.wifiStatus === "connecting")
|
if (Network.wifiStatus === "connecting")
|
||||||
return "wifi-4";
|
return "wifi-4";
|
||||||
|
|||||||
@@ -11,10 +11,9 @@ ScrollBar {
|
|||||||
active: hovered || pressed
|
active: hovered || pressed
|
||||||
property color color: Looks.colors.controlBg
|
property color color: Looks.colors.controlBg
|
||||||
|
|
||||||
contentItem: Rectangle {
|
contentItem: Pill {
|
||||||
implicitWidth: root.active ? 4 : 2
|
implicitWidth: root.active ? 4 : 2
|
||||||
implicitHeight: root.visualSize
|
implicitHeight: root.visualSize
|
||||||
radius: 9999
|
|
||||||
color: root.color
|
color: root.color
|
||||||
|
|
||||||
opacity: root.policy === ScrollBar.AlwaysOn || (root.active && root.size < 1.0) ? 0.5 : 0
|
opacity: root.policy === ScrollBar.AlwaysOn || (root.active && root.size < 1.0) ? 0.5 : 0
|
||||||
|
|||||||
@@ -1,31 +1,23 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import qs.modules.common.widgets as W
|
||||||
|
|
||||||
Item {
|
W.FixedWidthTextContainer {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string longestText
|
|
||||||
property alias text: textItem.text
|
property alias text: textItem.text
|
||||||
property alias font: textItem.font
|
|
||||||
property alias horizontalAlignment: textItem.horizontalAlignment
|
property alias horizontalAlignment: textItem.horizontalAlignment
|
||||||
property alias verticalAlignment: textItem.verticalAlignment
|
property alias verticalAlignment: textItem.verticalAlignment
|
||||||
property alias color: textItem.color
|
property alias color: textItem.color
|
||||||
|
|
||||||
implicitWidth: longestTextMetrics.width
|
font {
|
||||||
implicitHeight: longestTextMetrics.height
|
family: Looks.font.family.ui
|
||||||
|
pixelSize: Looks.font.pixelSize.large
|
||||||
TextMetrics {
|
weight: Looks.font.weight.regular
|
||||||
id: longestTextMetrics
|
|
||||||
text: root.longestText
|
|
||||||
font {
|
|
||||||
family: Looks.font.family.ui
|
|
||||||
pixelSize: Looks.font.pixelSize.large
|
|
||||||
weight: Looks.font.weight.regular
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WText {
|
WText {
|
||||||
id: textItem
|
id: textItem
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
font.pixelSize: Looks.font.pixelSize.large
|
font: root.font
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ BodyRectangle {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: {
|
text: {
|
||||||
var result = dayOfWeekItem.model.shortName;
|
var result = dayOfWeekItem.model.shortName;
|
||||||
if (Config.options.waffles.calendar.force2CharDayOfWeek) result = result.substring(0,2);
|
if (Config.options.calendar.force2CharDayOfWeek) result = result.substring(0,2);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
color: Looks.colors.fg
|
color: Looks.colors.fg
|
||||||
@@ -70,6 +70,7 @@ BodyRectangle {
|
|||||||
buttonSpacing: 6
|
buttonSpacing: 6
|
||||||
buttonVerticalSpacing: 1
|
buttonVerticalSpacing: 1
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
scrollAnimation: Looks.transition.scroll.createObject(this)
|
||||||
delegate: DayButton {}
|
delegate: DayButton {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.ii.background
|
||||||
|
import qs.modules.ii.cheatsheet
|
||||||
|
import qs.modules.ii.dock
|
||||||
|
import qs.modules.ii.lock
|
||||||
|
import qs.modules.ii.mediaControls
|
||||||
|
import qs.modules.ii.notificationPopup
|
||||||
|
import qs.modules.ii.onScreenDisplay
|
||||||
|
import qs.modules.ii.onScreenKeyboard
|
||||||
|
import qs.modules.ii.overview
|
||||||
|
import qs.modules.ii.polkit
|
||||||
|
import qs.modules.ii.regionSelector
|
||||||
|
import qs.modules.ii.screenCorners
|
||||||
|
import qs.modules.ii.screenTranslator
|
||||||
|
import qs.modules.ii.sessionScreen
|
||||||
|
import qs.modules.ii.sidebarLeft
|
||||||
|
import qs.modules.ii.sidebarRight
|
||||||
|
import qs.modules.ii.overlay
|
||||||
|
import qs.modules.ii.verticalBar
|
||||||
|
import qs.modules.ii.wallpaperSelector
|
||||||
|
|
||||||
|
import qs.modules.hefty.topLayer
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
// New
|
||||||
|
PanelLoader { component: HTopLayer {} }
|
||||||
|
|
||||||
|
// Fallbacks
|
||||||
|
PanelLoader { component: Background {} }
|
||||||
|
PanelLoader { component: Cheatsheet {} }
|
||||||
|
PanelLoader { extraCondition: Config.options.dock.enable; component: Dock {} }
|
||||||
|
PanelLoader { component: Lock {} }
|
||||||
|
PanelLoader { component: MediaControls {} }
|
||||||
|
PanelLoader { component: NotificationPopup {} }
|
||||||
|
PanelLoader { component: OnScreenDisplay {} }
|
||||||
|
PanelLoader { component: OnScreenKeyboard {} }
|
||||||
|
PanelLoader { component: Overlay {} }
|
||||||
|
PanelLoader { component: Overview {} }
|
||||||
|
PanelLoader { component: Polkit {} }
|
||||||
|
PanelLoader { component: RegionSelector {} }
|
||||||
|
PanelLoader { component: ScreenCorners {} }
|
||||||
|
PanelLoader { component: ScreenTranslator {} }
|
||||||
|
PanelLoader { component: SessionScreen {} }
|
||||||
|
PanelLoader { component: SidebarLeft {} }
|
||||||
|
PanelLoader { component: SidebarRight {} }
|
||||||
|
PanelLoader { component: WallpaperSelector {} }
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user