forked from Shinonome/dots-hyprland
Merge branch 'end-4:main' into main
This commit is contained in:
@@ -23,3 +23,5 @@ exec-once = wl-paste --type image --watch bash -c 'cliphist store && qs -c $qsCo
|
|||||||
# Cursor
|
# Cursor
|
||||||
exec-once = hyprctl setcursor Bibata-Modern-Classic 24
|
exec-once = hyprctl setcursor Bibata-Modern-Classic 24
|
||||||
|
|
||||||
|
# Fix dock pinned apps not launching properly (https://github.com/end-4/dots-hyprland/issues/2200)
|
||||||
|
exec-once = sleep 3.5 && hyprctl reload && sleep 0.5 && touch ~/.config/quickshell/ii/shell.qml
|
||||||
|
|||||||
@@ -14,9 +14,50 @@ Item {
|
|||||||
property real padding: 4
|
property real padding: 4
|
||||||
implicitWidth: row.implicitWidth + padding * 2
|
implicitWidth: row.implicitWidth + padding * 2
|
||||||
implicitHeight: row.implicitHeight + padding * 2
|
implicitHeight: row.implicitHeight + padding * 2
|
||||||
|
// Excellent symbol explaination and source :
|
||||||
|
// http://xahlee.info/comp/unicode_computing_symbols.html
|
||||||
|
// https://www.nerdfonts.com/cheat-sheet
|
||||||
|
property var macSymbolMap: ({
|
||||||
|
"Ctrl": "",
|
||||||
|
"Alt": "",
|
||||||
|
"Shift": "",
|
||||||
|
"Space": "",
|
||||||
|
"Tab": "↹",
|
||||||
|
"Equal": "",
|
||||||
|
"Minus": "",
|
||||||
|
"Print": "",
|
||||||
|
"BackSpace": "",
|
||||||
|
"Delete": "⌦",
|
||||||
|
"Return": "",
|
||||||
|
"Period": ".",
|
||||||
|
"Escape": "⎋"
|
||||||
|
})
|
||||||
|
property var functionSymbolMap: ({
|
||||||
|
"F1": "",
|
||||||
|
"F2": "",
|
||||||
|
"F3": "",
|
||||||
|
"F4": "",
|
||||||
|
"F5": "",
|
||||||
|
"F6": "",
|
||||||
|
"F7": "",
|
||||||
|
"F8": "",
|
||||||
|
"F9": "",
|
||||||
|
"F10": "",
|
||||||
|
"F11": "",
|
||||||
|
"F12": "",
|
||||||
|
})
|
||||||
|
|
||||||
|
property var mouseSymbolMap: ({
|
||||||
|
"mouse_up": "",
|
||||||
|
"mouse_down": "",
|
||||||
|
"mouse:272": "L",
|
||||||
|
"mouse:273": "R",
|
||||||
|
"Scroll ↑/↓": "",
|
||||||
|
"Page_↑/↓": "⇞/⇟",
|
||||||
|
})
|
||||||
|
|
||||||
property var keyBlacklist: ["Super_L"]
|
property var keyBlacklist: ["Super_L"]
|
||||||
property var keySubstitutions: ({
|
property var keySubstitutions: Object.assign({
|
||||||
"Super": "",
|
"Super": "",
|
||||||
"mouse_up": "Scroll ↓", // ikr, weird
|
"mouse_up": "Scroll ↓", // ikr, weird
|
||||||
"mouse_down": "Scroll ↑", // trust me bro
|
"mouse_down": "Scroll ↑", // trust me bro
|
||||||
@@ -27,7 +68,14 @@ Item {
|
|||||||
"Hash": "#",
|
"Hash": "#",
|
||||||
"Return": "Enter",
|
"Return": "Enter",
|
||||||
// "Shift": "",
|
// "Shift": "",
|
||||||
})
|
},
|
||||||
|
!!Config.options.cheatsheet.superKey ? {
|
||||||
|
"Super": Config.options.cheatsheet.superKey,
|
||||||
|
}: {},
|
||||||
|
Config.options.cheatsheet.useMacSymbol ? macSymbolMap : {},
|
||||||
|
Config.options.cheatsheet.useFnSymbol ? functionSymbolMap : {},
|
||||||
|
Config.options.cheatsheet.useMouseSymbol ? mouseSymbolMap : {},
|
||||||
|
)
|
||||||
|
|
||||||
Row { // Keybind columns
|
Row { // Keybind columns
|
||||||
id: row
|
id: row
|
||||||
@@ -77,6 +125,17 @@ Item {
|
|||||||
var result = [];
|
var result = [];
|
||||||
for (var i = 0; i < keybindSection.modelData.keybinds.length; i++) {
|
for (var i = 0; i < keybindSection.modelData.keybinds.length; i++) {
|
||||||
const keybind = keybindSection.modelData.keybinds[i];
|
const keybind = keybindSection.modelData.keybinds[i];
|
||||||
|
|
||||||
|
if (!Config.options.cheatsheet.splitButtons) {
|
||||||
|
|
||||||
|
for (var j = 0; j < keybind.mods.length; j++) {
|
||||||
|
keybind.mods[j] = keySubstitutions[keybind.mods[j]] || keybind.mods[j];
|
||||||
|
}
|
||||||
|
keybind.mods = [keybind.mods.join(' ') ]
|
||||||
|
keybind.mods[0] += !keyBlacklist.includes(keybind.key) && keybind.mods[0].length ? ' ' : ''
|
||||||
|
keybind.mods[0] += !keyBlacklist.includes(keybind.key) ? (keySubstitutions[keybind.key] || keybind.key) : ''
|
||||||
|
}
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
"type": "keys",
|
"type": "keys",
|
||||||
"mods": keybind.mods,
|
"mods": keybind.mods,
|
||||||
@@ -108,17 +167,19 @@ Item {
|
|||||||
delegate: KeyboardKey {
|
delegate: KeyboardKey {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
key: keySubstitutions[modelData] || modelData
|
key: keySubstitutions[modelData] || modelData
|
||||||
|
pixelSize: Config.options.cheatsheet.fontSize.key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StyledText {
|
StyledText {
|
||||||
id: keybindPlus
|
id: keybindPlus
|
||||||
visible: !keyBlacklist.includes(modelData.key) && modelData.mods.length > 0
|
visible: Config.options.cheatsheet.splitButtons && !keyBlacklist.includes(modelData.key) && modelData.mods.length > 0
|
||||||
text: "+"
|
text: "+"
|
||||||
}
|
}
|
||||||
KeyboardKey {
|
KeyboardKey {
|
||||||
id: keybindKey
|
id: keybindKey
|
||||||
visible: !keyBlacklist.includes(modelData.key)
|
visible: Config.options.cheatsheet.splitButtons && !keyBlacklist.includes(modelData.key)
|
||||||
key: keySubstitutions[modelData.key] || modelData.key
|
key: keySubstitutions[modelData.key] || modelData.key
|
||||||
|
pixelSize: Config.options.cheatsheet.fontSize.key
|
||||||
color: Appearance.colors.colOnLayer0
|
color: Appearance.colors.colOnLayer0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +195,7 @@ Item {
|
|||||||
StyledText {
|
StyledText {
|
||||||
id: commentText
|
id: commentText
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
font.pixelSize: Config.options.cheatsheet.fontSize.comment || Appearance.font.pixelSize.smaller
|
||||||
text: modelData.comment
|
text: modelData.comment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,4 +213,4 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,6 +267,22 @@ Singleton {
|
|||||||
property int suspend: 3
|
property int suspend: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property JsonObject cheatsheet: JsonObject {
|
||||||
|
// Use a nerdfont to see the icons
|
||||||
|
// 0: | 1: | 2: | 3: | 4:
|
||||||
|
// 5: | 6: | 7: | 8: | 9:
|
||||||
|
// 10: | 11: | 12: | 13: | 14:
|
||||||
|
property string superKey: ""
|
||||||
|
property bool useMacSymbol: false
|
||||||
|
property bool splitButtons: true
|
||||||
|
property bool useMouseSymbol: false
|
||||||
|
property bool useFnSymbol: false
|
||||||
|
property JsonObject fontSize: JsonObject {
|
||||||
|
property int key: Appearance.font.pixelSize.smaller
|
||||||
|
property int comment: Appearance.font.pixelSize.smaller
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
property JsonObject conflictKiller: JsonObject {
|
property JsonObject conflictKiller: JsonObject {
|
||||||
property bool autoKillNotificationDaemons: false
|
property bool autoKillNotificationDaemons: false
|
||||||
property bool autoKillTrays: false
|
property bool autoKillTrays: false
|
||||||
@@ -364,6 +380,7 @@ Singleton {
|
|||||||
property JsonObject overlay: JsonObject {
|
property JsonObject overlay: JsonObject {
|
||||||
property bool openingZoomAnimation: true
|
property bool openingZoomAnimation: true
|
||||||
property bool darkenScreen: true
|
property bool darkenScreen: true
|
||||||
|
property real clickthroughOpacity: 0.7
|
||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject overview: JsonObject {
|
property JsonObject overview: JsonObject {
|
||||||
@@ -395,6 +412,7 @@ Singleton {
|
|||||||
|
|
||||||
property JsonObject resources: JsonObject {
|
property JsonObject resources: JsonObject {
|
||||||
property int updateInterval: 3000
|
property int updateInterval: 3000
|
||||||
|
property int historyLength: 60
|
||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject musicRecognition: JsonObject {
|
property JsonObject musicRecognition: JsonObject {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject overlay: JsonObject {
|
property JsonObject overlay: JsonObject {
|
||||||
property list<string> open: ["crosshair"]
|
property list<string> open: ["crosshair", "recorder", "volumeMixer", "resources"]
|
||||||
property JsonObject crosshair: JsonObject {
|
property JsonObject crosshair: JsonObject {
|
||||||
property bool pinned: false
|
property bool pinned: false
|
||||||
property bool clickthrough: true
|
property bool clickthrough: true
|
||||||
@@ -90,14 +90,28 @@ Singleton {
|
|||||||
property JsonObject recorder: JsonObject {
|
property JsonObject recorder: JsonObject {
|
||||||
property bool pinned: false
|
property bool pinned: false
|
||||||
property bool clickthrough: false
|
property bool clickthrough: false
|
||||||
property real x: 100
|
property real x: 80
|
||||||
property real y: 130
|
property real y: 80
|
||||||
|
}
|
||||||
|
property JsonObject resources: JsonObject {
|
||||||
|
property bool pinned: false
|
||||||
|
property bool clickthrough: true
|
||||||
|
property real x: 1500
|
||||||
|
property real y: 770
|
||||||
|
property int tabIndex: 0
|
||||||
}
|
}
|
||||||
property JsonObject volumeMixer: JsonObject {
|
property JsonObject volumeMixer: JsonObject {
|
||||||
property bool pinned: false
|
property bool pinned: false
|
||||||
property bool clickthrough: false
|
property bool clickthrough: false
|
||||||
property real x: 100
|
property real x: 80
|
||||||
property real y: 320
|
property real y: 280
|
||||||
|
property int tabIndex: 0
|
||||||
|
}
|
||||||
|
property JsonObject fpsLimiter: JsonObject {
|
||||||
|
property bool pinned: false
|
||||||
|
property bool clickthrough: false
|
||||||
|
property real x: 1576
|
||||||
|
property real y: 630
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.functions
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple one value line graph
|
||||||
|
*/
|
||||||
|
Canvas {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
enum Alignment { Left, Right }
|
||||||
|
|
||||||
|
required property list<real> values
|
||||||
|
property int points: values.length
|
||||||
|
property color color: Appearance.colors.colPrimary
|
||||||
|
property real fillOpacity: 0.5
|
||||||
|
property var alignment: Graph.Alignment.Left
|
||||||
|
|
||||||
|
onValuesChanged: root.requestPaint()
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d")
|
||||||
|
ctx.clearRect(0, 0, width, height)
|
||||||
|
if (!root.values || root.values.length < 2)
|
||||||
|
return
|
||||||
|
|
||||||
|
var n = root.points
|
||||||
|
var dx = width / (n - 1)
|
||||||
|
ctx.strokeStyle = root.color
|
||||||
|
ctx.fillStyle = ColorUtils.transparentize(root.color, 1 - root.fillOpacity)
|
||||||
|
ctx.lineWidth = 2
|
||||||
|
ctx.beginPath()
|
||||||
|
for (var i = 0; i < n; ++i) {
|
||||||
|
var valueIndex = (root.alignment === Graph.Alignment.Right) ? root.values.length - n + i : i
|
||||||
|
if (valueIndex < 0 || valueIndex >= root.values.length) {
|
||||||
|
continue; // No data for this point
|
||||||
|
}
|
||||||
|
var x = i * dx
|
||||||
|
var norm = root.values[valueIndex] // already in 0-1 range
|
||||||
|
var y = height - norm * height
|
||||||
|
if (valueIndex === 0) {
|
||||||
|
ctx.moveTo(x, height)
|
||||||
|
ctx.lineTo(x, y)
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.stroke()
|
||||||
|
ctx.lineTo(width, height)
|
||||||
|
ctx.fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -117,7 +117,7 @@ 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,6 +130,9 @@ 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
|
||||||
|
border.color: Appearance.colors.colSecondary
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: StyledText {
|
contentItem: StyledText {
|
||||||
|
|||||||
@@ -18,5 +18,6 @@ ToolbarButton {
|
|||||||
iconSize: 22
|
iconSize: 22
|
||||||
text: iconBtn.text
|
text: iconBtn.text
|
||||||
color: iconBtn.colText
|
color: iconBtn.colText
|
||||||
|
animateChange: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ Rectangle {
|
|||||||
property real extraBottomBorderWidth: 2
|
property real extraBottomBorderWidth: 2
|
||||||
property color borderColor: Appearance.colors.colOnLayer0
|
property color borderColor: Appearance.colors.colOnLayer0
|
||||||
property real borderRadius: 5
|
property real borderRadius: 5
|
||||||
|
property real pixelSize: Appearance.font.pixelSize.smaller
|
||||||
property color keyColor: Appearance.m3colors.m3surfaceContainerLow
|
property color keyColor: Appearance.m3colors.m3surfaceContainerLow
|
||||||
implicitWidth: keyFace.implicitWidth + borderWidth * 2
|
implicitWidth: keyFace.implicitWidth + borderWidth * 2
|
||||||
implicitHeight: keyFace.implicitHeight + borderWidth * 2 + extraBottomBorderWidth
|
implicitHeight: keyFace.implicitHeight + borderWidth * 2 + extraBottomBorderWidth
|
||||||
@@ -35,7 +36,7 @@ Rectangle {
|
|||||||
id: keyText
|
id: keyText
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
font.family: Appearance.font.family.monospace
|
font.family: Appearance.font.family.monospace
|
||||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
font.pixelSize: root.pixelSize
|
||||||
text: key
|
text: key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.models
|
||||||
|
|
||||||
|
TabBar {
|
||||||
|
id: root
|
||||||
|
property real indicatorPadding: 8
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
WheelHandler {
|
||||||
|
onWheel: (event) => {
|
||||||
|
if (event.angleDelta.y < 0) root.incrementCurrentIndex();
|
||||||
|
else if (event.angleDelta.y > 0) root.decrementCurrentIndex();
|
||||||
|
}
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: activeIndicator
|
||||||
|
z: 9999
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
topLeftRadius: height
|
||||||
|
topRightRadius: height
|
||||||
|
bottomLeftRadius: 0
|
||||||
|
bottomRightRadius: 0
|
||||||
|
color: Appearance.colors.colPrimary
|
||||||
|
// Animation
|
||||||
|
property real baseWidth: root.width / root.count
|
||||||
|
AnimatedTabIndexPair {
|
||||||
|
id: idxPair
|
||||||
|
index: root.currentIndex
|
||||||
|
}
|
||||||
|
height: 3
|
||||||
|
x: Math.min(idxPair.idx1, idxPair.idx2) * baseWidth + root.indicatorPadding
|
||||||
|
width: ((Math.max(idxPair.idx1, idxPair.idx2) + 1) * baseWidth - root.indicatorPadding) - x
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle { // Tabbar bottom border
|
||||||
|
id: tabBarBottomBorder
|
||||||
|
z: 9998
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
height: 1
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
color: Appearance.colors.colOutlineVariant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
import qs.modules.common.functions
|
||||||
|
import qs.modules.common.widgets
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
@@ -10,14 +10,12 @@ TabButton {
|
|||||||
id: root
|
id: root
|
||||||
property string buttonText
|
property string buttonText
|
||||||
property string buttonIcon
|
property string buttonIcon
|
||||||
property bool selected: false
|
|
||||||
property int rippleDuration: 1200
|
property int rippleDuration: 1200
|
||||||
height: buttonBackground.height
|
|
||||||
property int tabContentWidth: buttonBackground.width - buttonBackground.radius*2
|
property int tabContentWidth: buttonBackground.width - buttonBackground.radius*2
|
||||||
|
|
||||||
property color colBackground: ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
property color colBackground: ColorUtils.transparentize(Appearance.colors.colSurfaceContainer)
|
||||||
property color colBackgroundHover: Appearance.colors.colLayer1Hover
|
property color colBackgroundHover: ColorUtils.transparentize(Appearance.colors.colOnSurface, root.checked ? 1 : 0.95)
|
||||||
property color colRipple: Appearance.colors.colLayer1Active
|
property color colRipple: ColorUtils.transparentize(Appearance.colors.colOnSurface, 0.95)
|
||||||
|
|
||||||
PointingHandInteraction {}
|
PointingHandInteraction {}
|
||||||
|
|
||||||
@@ -91,8 +89,12 @@ TabButton {
|
|||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: buttonBackground
|
id: buttonBackground
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 3
|
||||||
|
}
|
||||||
radius: Appearance?.rounding.normal
|
radius: Appearance?.rounding.normal
|
||||||
implicitHeight: 37
|
implicitHeight: 42
|
||||||
color: (root.hovered ? root.colBackgroundHover : root.colBackground)
|
color: (root.hovered ? root.colBackgroundHover : root.colBackground)
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: OpacityMask {
|
layer.effect: OpacityMask {
|
||||||
@@ -156,8 +158,8 @@ TabButton {
|
|||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
text: buttonIcon
|
text: buttonIcon
|
||||||
iconSize: Appearance.font.pixelSize.huge
|
iconSize: Appearance.font.pixelSize.huge
|
||||||
fill: selected ? 1 : 0
|
fill: root.checked ? 1 : 0
|
||||||
color: selected ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1
|
color: root.checked ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
}
|
}
|
||||||
@@ -167,7 +169,7 @@ TabButton {
|
|||||||
id: buttonTextWidget
|
id: buttonTextWidget
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
font.pixelSize: Appearance.font.pixelSize.small
|
font.pixelSize: Appearance.font.pixelSize.small
|
||||||
color: selected ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1
|
color: root.checked ? Appearance.colors.colPrimary : Appearance.colors.colOnLayer1
|
||||||
text: buttonText
|
text: buttonText
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Scope {
|
|||||||
exclusionMode: ExclusionMode.Ignore
|
exclusionMode: ExclusionMode.Ignore
|
||||||
WlrLayershell.namespace: "quickshell:overlay"
|
WlrLayershell.namespace: "quickshell:overlay"
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
WlrLayershell.keyboardFocus: GlobalStates.overlayOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||||
visible: true
|
visible: true
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
@@ -43,6 +43,30 @@ Scope {
|
|||||||
right: true
|
right: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
id: grab
|
||||||
|
windows: [overlayWindow]
|
||||||
|
active: false
|
||||||
|
onCleared: () => {
|
||||||
|
if (!active) GlobalStates.overlayOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: GlobalStates
|
||||||
|
function onOverlayOpenChanged() {
|
||||||
|
delayedGrabTimer.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: delayedGrabTimer
|
||||||
|
interval: Appearance.animation.elementMoveFast.duration
|
||||||
|
onTriggered: {
|
||||||
|
grab.active = GlobalStates.overlayOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OverlayContent {
|
OverlayContent {
|
||||||
id: overlayContent
|
id: overlayContent
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import qs.modules.overlay.crosshair
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
focus: true
|
||||||
readonly property bool usePasswordChars: !PolkitService.flow?.responseVisible ?? true
|
readonly property bool usePasswordChars: !PolkitService.flow?.responseVisible ?? true
|
||||||
|
|
||||||
Keys.onPressed: (event) => { // Esc to close
|
Keys.onPressed: (event) => { // Esc to close
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ Singleton {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property list<var> availableWidgets: [
|
readonly property list<var> availableWidgets: [
|
||||||
{ identifier: "crosshair", materialSymbol: "point_scan" },
|
|
||||||
{ identifier: "volumeMixer", materialSymbol: "volume_up" },
|
|
||||||
{ identifier: "recorder", materialSymbol: "screen_record" },
|
{ identifier: "recorder", materialSymbol: "screen_record" },
|
||||||
|
{ identifier: "volumeMixer", materialSymbol: "volume_up" },
|
||||||
|
{ identifier: "crosshair", materialSymbol: "point_scan" },
|
||||||
|
{ identifier: "fpsLimiter", materialSymbol: "animation" },
|
||||||
|
{ identifier: "resources", materialSymbol: "browse_activity" }
|
||||||
]
|
]
|
||||||
|
|
||||||
readonly property bool hasPinnedWidgets: root.pinnedWidgetIdentifiers.length > 0
|
readonly property bool hasPinnedWidgets: root.pinnedWidgetIdentifiers.length > 0
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import Quickshell
|
|||||||
import Quickshell.Bluetooth
|
import Quickshell.Bluetooth
|
||||||
import qs.modules.overlay.crosshair
|
import qs.modules.overlay.crosshair
|
||||||
import qs.modules.overlay.volumeMixer
|
import qs.modules.overlay.volumeMixer
|
||||||
|
import qs.modules.overlay.fpsLimiter
|
||||||
import qs.modules.overlay.recorder
|
import qs.modules.overlay.recorder
|
||||||
|
import qs.modules.overlay.resources
|
||||||
|
|
||||||
DelegateChooser {
|
DelegateChooser {
|
||||||
id: root
|
id: root
|
||||||
@@ -16,5 +18,7 @@ DelegateChooser {
|
|||||||
|
|
||||||
DelegateChoice { roleValue: "crosshair"; Crosshair {} }
|
DelegateChoice { roleValue: "crosshair"; Crosshair {} }
|
||||||
DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} }
|
DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} }
|
||||||
|
DelegateChoice { roleValue: "fpsLimiter"; FpsLimiter {} }
|
||||||
DelegateChoice { roleValue: "recorder"; Recorder {} }
|
DelegateChoice { roleValue: "recorder"; Recorder {} }
|
||||||
|
DelegateChoice { roleValue: "resources"; Resources {} }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ AbstractOverlayWidget {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property Item contentItem
|
required property Item contentItem
|
||||||
|
property bool fancyBorders: true
|
||||||
|
property bool showCenterButton: false
|
||||||
|
property bool showClickabilityButton: true
|
||||||
|
|
||||||
required property var modelData
|
required property var modelData
|
||||||
readonly property string identifier: modelData.identifier
|
readonly property string identifier: modelData.identifier
|
||||||
@@ -29,6 +32,8 @@ AbstractOverlayWidget {
|
|||||||
property var persistentStateEntry: Persistent.states.overlay[identifier]
|
property var persistentStateEntry: Persistent.states.overlay[identifier]
|
||||||
property real radius: Appearance.rounding.windowRounding
|
property real radius: Appearance.rounding.windowRounding
|
||||||
property real minWidth: 250
|
property real minWidth: 250
|
||||||
|
property real padding: 6
|
||||||
|
property real contentRadius: radius - padding
|
||||||
|
|
||||||
draggable: GlobalStates.overlayOpen
|
draggable: GlobalStates.overlayOpen
|
||||||
x: Math.round(persistentStateEntry.x) // Round or it'll be blurry
|
x: Math.round(persistentStateEntry.x) // Round or it'll be blurry
|
||||||
@@ -38,9 +43,10 @@ AbstractOverlayWidget {
|
|||||||
drag {
|
drag {
|
||||||
minimumX: 0
|
minimumX: 0
|
||||||
minimumY: 0
|
minimumY: 0
|
||||||
maximumX: root.parent.width - root.width
|
maximumX: root.parent?.width - root.width
|
||||||
maximumY: root.parent.height - root.height
|
maximumY: root.parent?.height - root.height
|
||||||
}
|
}
|
||||||
|
opacity: (GlobalStates.overlayOpen || !clickthrough) ? 1.0 : Config.options.overlay.clickthroughOpacity
|
||||||
|
|
||||||
// Guarded states & registration funcs
|
// Guarded states & registration funcs
|
||||||
property bool open: Persistent.states.overlay.open
|
property bool open: Persistent.states.overlay.open
|
||||||
@@ -96,11 +102,15 @@ AbstractOverlayWidget {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: border
|
id: border
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "transparent"
|
color: (root.fancyBorders && GlobalStates.overlayOpen) ? Appearance.colors.colLayer1 : "transparent"
|
||||||
radius: root.radius
|
radius: root.radius
|
||||||
border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1)
|
border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
layer.enabled: GlobalStates.overlayOpen
|
layer.enabled: GlobalStates.overlayOpen
|
||||||
layer.effect: OpacityMask {
|
layer.effect: OpacityMask {
|
||||||
maskSource: Rectangle {
|
maskSource: Rectangle {
|
||||||
@@ -110,25 +120,23 @@ AbstractOverlayWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
ColumnLayout {
|
||||||
id: contentColumn
|
id: contentColumn
|
||||||
z: -1
|
z: root.fancyBorders ? 0 : -1
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
// Title bar
|
// Title bar
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: titleBar
|
id: titleBar
|
||||||
opacity: GlobalStates.overlayOpen ? 1 : 0
|
opacity: GlobalStates.overlayOpen ? 1 : 0
|
||||||
anchors {
|
Layout.fillWidth: true
|
||||||
left: parent.left
|
property real padding: 6
|
||||||
right: parent.right
|
|
||||||
}
|
|
||||||
property real padding: 2
|
|
||||||
implicitWidth: titleBarRow.implicitWidth + padding * 2
|
implicitWidth: titleBarRow.implicitWidth + padding * 2
|
||||||
implicitHeight: titleBarRow.implicitHeight + padding * 2
|
implicitHeight: titleBarRow.implicitHeight + padding * 2
|
||||||
color: Appearance.m3colors.m3surfaceContainer
|
color: root.fancyBorders ? "transparent" : Appearance.colors.colLayer1
|
||||||
border.color: Appearance.colors.colOutlineVariant
|
// border.color: Appearance.colors.colOutlineVariant
|
||||||
border.width: 1
|
// border.width: 1
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: titleBarRow
|
id: titleBarRow
|
||||||
@@ -136,8 +144,9 @@ AbstractOverlayWidget {
|
|||||||
fill: parent
|
fill: parent
|
||||||
margins: titleBar.padding
|
margins: titleBar.padding
|
||||||
leftMargin: titleBar.padding + 8
|
leftMargin: titleBar.padding + 8
|
||||||
|
bottomMargin: root.fancyBorders ? 0 : titleBar.padding
|
||||||
}
|
}
|
||||||
spacing: 0
|
spacing: 2
|
||||||
|
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
text: root.materialSymbol
|
text: root.materialSymbol
|
||||||
@@ -153,6 +162,7 @@ AbstractOverlayWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TitlebarButton {
|
TitlebarButton {
|
||||||
|
visible: root.showCenterButton
|
||||||
materialSymbol: "recenter"
|
materialSymbol: "recenter"
|
||||||
onClicked: root.center()
|
onClicked: root.center()
|
||||||
StyledToolTip {
|
StyledToolTip {
|
||||||
@@ -161,6 +171,7 @@ AbstractOverlayWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TitlebarButton {
|
TitlebarButton {
|
||||||
|
visible: (root.pinned && root.showClickabilityButton)
|
||||||
materialSymbol: "mouse"
|
materialSymbol: "mouse"
|
||||||
toggled: !root.clickthrough
|
toggled: !root.clickthrough
|
||||||
onClicked: root.toggleClickthrough()
|
onClicked: root.toggleClickthrough()
|
||||||
@@ -191,7 +202,10 @@ AbstractOverlayWidget {
|
|||||||
// Content
|
// Content
|
||||||
Item {
|
Item {
|
||||||
id: contentContainer
|
id: contentContainer
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.margins: root.fancyBorders ? root.padding : 0
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
implicitWidth: root.contentItem.implicitWidth
|
implicitWidth: root.contentItem.implicitWidth
|
||||||
implicitHeight: root.contentItem.implicitHeight
|
implicitHeight: root.contentItem.implicitHeight
|
||||||
children: [root.contentItem]
|
children: [root.contentItem]
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.overlay
|
import qs.modules.overlay
|
||||||
|
|
||||||
StyledOverlayWidget {
|
StyledOverlayWidget {
|
||||||
id: root
|
id: root
|
||||||
contentItem: CrosshairContent {}
|
fancyBorders: false // Crosshair should be see-through
|
||||||
|
showCenterButton: true
|
||||||
|
opacity: 1 // The crosshair itself already has transparency if configured
|
||||||
|
showClickabilityButton: false
|
||||||
|
clickthrough: true
|
||||||
|
|
||||||
|
contentItem: CrosshairContent {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.overlay
|
||||||
|
|
||||||
|
StyledOverlayWidget {
|
||||||
|
id: root
|
||||||
|
title: "MangoHud FPS"
|
||||||
|
contentItem: FpsLimiterContent {
|
||||||
|
radius: root.contentRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import qs.services
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
enum State { Normal, Success, Error }
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
property real padding: 16
|
||||||
|
property var currentState: FpsLimiterContent.State.Normal
|
||||||
|
color: Appearance.m3colors.m3surfaceContainer
|
||||||
|
implicitWidth: content.implicitWidth + (padding * 2)
|
||||||
|
implicitHeight: content.implicitHeight + (padding * 2)
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: iconResetTimer
|
||||||
|
interval: 1000
|
||||||
|
onTriggered: {
|
||||||
|
root.currentState = FpsLimiterContent.State.Normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyLimit() {
|
||||||
|
var fpsValue = parseInt(fpsField.text);
|
||||||
|
if (isNaN(fpsValue) || fpsValue < 0) {
|
||||||
|
root.currentState = FpsLimiterContent.State.Error;
|
||||||
|
iconResetTimer.restart();
|
||||||
|
fpsField.text = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfgPaths = [
|
||||||
|
"~/.config/MangoHud/MangoHud.conf",
|
||||||
|
]; // MangoHud config files
|
||||||
|
|
||||||
|
var updateCommands = cfgPaths.map(path => {
|
||||||
|
return "if grep -q '^fps_limit=' " + path + "; " +
|
||||||
|
"then sed -i 's/^fps_limit=.*/fps_limit=" + fpsValue + "/' " + path + "; " +
|
||||||
|
"else echo 'fps_limit=" + fpsValue + "' >> " + path + "; fi";
|
||||||
|
}).join("; ");
|
||||||
|
|
||||||
|
var cmd = updateCommands + "; pkill -SIGUSR2 mangohud";
|
||||||
|
|
||||||
|
fpsSetter.command = ["bash", "-c", cmd];
|
||||||
|
fpsSetter.startDetached();
|
||||||
|
|
||||||
|
root.currentState = FpsLimiterContent.State.Success;
|
||||||
|
iconResetTimer.restart();
|
||||||
|
|
||||||
|
// Clear the field after applying
|
||||||
|
fpsField.text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: fpsSetter
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: content
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
ToolbarTextField {
|
||||||
|
id: fpsField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredWidth: 200
|
||||||
|
placeholderText: root.currentState === FpsLimiterContent.State.Error ? Translation.tr("Enter a valid number") : Translation.tr("Set FPS limit")
|
||||||
|
inputMethodHints: Qt.ImhDigitsOnly
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
root.applyLimit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconToolbarButton {
|
||||||
|
id: applyButton
|
||||||
|
text: switch (root.currentState) {
|
||||||
|
case FpsLimiterContent.State.Error: return "close";
|
||||||
|
case FpsLimiterContent.State.Success: return "check";
|
||||||
|
case FpsLimiterContent.State.Normal:
|
||||||
|
default: return "save";
|
||||||
|
}
|
||||||
|
enabled: root.currentState === FpsLimiterContent.State.Normal && fpsField.text.length > 0
|
||||||
|
onClicked: {
|
||||||
|
root.applyLimit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs
|
import qs
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
@@ -14,6 +13,7 @@ StyledOverlayWidget {
|
|||||||
contentItem: Rectangle {
|
contentItem: Rectangle {
|
||||||
id: contentItem
|
id: contentItem
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
radius: root.contentRadius
|
||||||
color: Appearance.m3colors.m3surfaceContainer
|
color: Appearance.m3colors.m3surfaceContainer
|
||||||
property real padding: 8
|
property real padding: 8
|
||||||
implicitHeight: contentColumn.implicitHeight + padding * 2
|
implicitHeight: contentColumn.implicitHeight + padding * 2
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Qt.labs.synchronizer
|
||||||
|
import qs
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.overlay
|
||||||
|
|
||||||
|
StyledOverlayWidget {
|
||||||
|
id: root
|
||||||
|
property list<var> resources: [
|
||||||
|
{
|
||||||
|
"icon": "planner_review",
|
||||||
|
"name": Translation.tr("CPU"),
|
||||||
|
"history": ResourceUsage.cpuUsageHistory,
|
||||||
|
"maxAvailableString": ResourceUsage.maxAvailableCpuString
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "memory",
|
||||||
|
"name": Translation.tr("RAM"),
|
||||||
|
"history": ResourceUsage.memoryUsageHistory,
|
||||||
|
"maxAvailableString": ResourceUsage.maxAvailableMemoryString
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "swap_horiz",
|
||||||
|
"name": Translation.tr("Swap"),
|
||||||
|
"history": ResourceUsage.swapUsageHistory,
|
||||||
|
"maxAvailableString": ResourceUsage.maxAvailableSwapString
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
contentItem: Rectangle {
|
||||||
|
id: contentItem
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: Appearance.m3colors.m3surfaceContainer
|
||||||
|
radius: root.contentRadius
|
||||||
|
property real padding: 4
|
||||||
|
implicitWidth: 350
|
||||||
|
implicitHeight: 200
|
||||||
|
// implicitHeight: contentColumn.implicitHeight + padding * 2
|
||||||
|
ColumnLayout {
|
||||||
|
id: contentColumn
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: parent.padding
|
||||||
|
}
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
SecondaryTabBar {
|
||||||
|
id: tabBar
|
||||||
|
|
||||||
|
currentIndex: Persistent.states.overlay.resources.tabIndex
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
Persistent.states.overlay.resources.tabIndex = tabBar.currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.resources.length
|
||||||
|
delegate: SecondaryTabButton {
|
||||||
|
required property int index
|
||||||
|
property var modelData: root.resources[index]
|
||||||
|
buttonIcon: modelData.icon
|
||||||
|
buttonText: modelData.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceSummary {
|
||||||
|
Layout.margins: 8
|
||||||
|
history: root.resources[tabBar.currentIndex]?.history ?? []
|
||||||
|
maxAvailableString: root.resources[tabBar.currentIndex]?.maxAvailableString ?? "--"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component ResourceSummary: RowLayout {
|
||||||
|
id: resourceSummary
|
||||||
|
required property list<real> history
|
||||||
|
required property string maxAvailableString
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 2
|
||||||
|
StyledText {
|
||||||
|
text: (resourceSummary.history[resourceSummary.history.length - 1] * 100).toFixed(1) + "%"
|
||||||
|
font {
|
||||||
|
family: Appearance.font.family.numbers
|
||||||
|
variableAxes: Appearance.font.variableAxes.numbers
|
||||||
|
pixelSize: Appearance.font.pixelSize.huge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StyledText {
|
||||||
|
text: Translation.tr("of %1").arg(resourceSummary.maxAvailableString)
|
||||||
|
font {
|
||||||
|
// family: Appearance.font.family.numbers
|
||||||
|
// variableAxes: Appearance.font.variableAxes.numbers
|
||||||
|
pixelSize: Appearance.font.pixelSize.smallie
|
||||||
|
}
|
||||||
|
color: Appearance.colors.colSubtext
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: graphBg
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
color: Appearance.colors.colSecondaryContainer
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: graphBg.width
|
||||||
|
height: graphBg.height
|
||||||
|
radius: graphBg.radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Graph {
|
||||||
|
anchors.fill: parent
|
||||||
|
values: root.resources[tabBar.currentIndex]?.history ?? []
|
||||||
|
points: ResourceUsage.historyLength
|
||||||
|
alignment: Graph.Alignment.Right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import qs.services
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
import qs.modules.overlay
|
import qs.modules.overlay
|
||||||
import qs.modules.sidebarRight.volumeMixer
|
import qs.modules.sidebarRight.volumeMixer
|
||||||
|
|
||||||
@@ -10,15 +13,49 @@ StyledOverlayWidget {
|
|||||||
contentItem: Rectangle {
|
contentItem: Rectangle {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
color: Appearance.m3colors.m3surfaceContainer
|
color: Appearance.m3colors.m3surfaceContainer
|
||||||
|
radius: root.contentRadius
|
||||||
property real padding: 16
|
property real padding: 16
|
||||||
implicitHeight: 600
|
implicitHeight: 600
|
||||||
implicitWidth: 350
|
implicitWidth: 350
|
||||||
|
|
||||||
VolumeDialogContent {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
id: contentColumn
|
||||||
anchors.margins: parent.padding
|
anchors {
|
||||||
isSink: true
|
fill: parent
|
||||||
}
|
margins: parent.padding
|
||||||
|
}
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
SecondaryTabBar {
|
||||||
|
id: tabBar
|
||||||
|
|
||||||
|
currentIndex: Persistent.states.overlay.volumeMixer.tabIndex
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
Persistent.states.overlay.volumeMixer.tabIndex = tabBar.currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
SecondaryTabButton {
|
||||||
|
buttonIcon: "media_output"
|
||||||
|
buttonText: Translation.tr("Output")
|
||||||
|
}
|
||||||
|
SecondaryTabButton {
|
||||||
|
buttonIcon: "mic"
|
||||||
|
buttonText: Translation.tr("Input")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SwipeView {
|
||||||
|
id: swipeView
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
currentIndex: Persistent.states.overlay.volumeMixer.tabIndex
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
Persistent.states.overlay.volumeMixer.tabIndex = swipeView.currentIndex;
|
||||||
|
}
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
VolumeDialogContent { isSink: true }
|
||||||
|
VolumeDialogContent { isSink: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,4 +90,6 @@ ContentPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,98 @@ import qs.modules.common.widgets
|
|||||||
ContentPage {
|
ContentPage {
|
||||||
forceWidth: true
|
forceWidth: true
|
||||||
|
|
||||||
|
ContentSection {
|
||||||
|
icon: "keyboard"
|
||||||
|
title: Translation.tr("Cheat sheet")
|
||||||
|
|
||||||
|
ContentSubsection {
|
||||||
|
title: Translation.tr("Super key symbol")
|
||||||
|
tooltip: Translation.tr("You can also manually edit cheatsheet.superKey")
|
||||||
|
ConfigSelectionArray {
|
||||||
|
currentValue: Config.options.cheatsheet.superKey
|
||||||
|
onSelected: newValue => {
|
||||||
|
Config.options.cheatsheet.superKey = newValue;
|
||||||
|
}
|
||||||
|
// Use a nerdfont to see the icons
|
||||||
|
options: ([
|
||||||
|
"", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "⌘", "", "", ""
|
||||||
|
]).map(icon => { return {
|
||||||
|
displayName: icon,
|
||||||
|
value: icon
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: ""
|
||||||
|
text: Translation.tr("Use macOS-like symbols for mods keys")
|
||||||
|
checked: Config.options.cheatsheet.useMacSymbol
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.cheatsheet.useMacSymbol = checked;
|
||||||
|
}
|
||||||
|
StyledToolTip {
|
||||||
|
text: Translation.tr("e.g. for Ctrl, for Alt, for Shift, etc")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: ""
|
||||||
|
text: Translation.tr("Use symbols for function keys")
|
||||||
|
checked: Config.options.cheatsheet.useFnSymbol
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.cheatsheet.useFnSymbol = checked;
|
||||||
|
}
|
||||||
|
StyledToolTip {
|
||||||
|
text: Translation.tr("e.g. for F1, for F12")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: ""
|
||||||
|
text: Translation.tr("Use symbols for mouse")
|
||||||
|
checked: Config.options.cheatsheet.useMouseSymbol
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.cheatsheet.useMouseSymbol = checked;
|
||||||
|
}
|
||||||
|
StyledToolTip {
|
||||||
|
text: Translation.tr("Replace for \"Scroll ↓\", \"Scroll ↑\", L \"LMB\", R \"RMB\", \"Scroll ↑/↓\" and ⇞/⇟ for \"Page_↑/↓\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "highlight_keyboard_focus"
|
||||||
|
text: Translation.tr("Split buttons")
|
||||||
|
checked: Config.options.cheatsheet.splitButtons
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.cheatsheet.splitButtons = checked;
|
||||||
|
}
|
||||||
|
StyledToolTip {
|
||||||
|
text: Translation.tr("Display modifiers and keys in multiple keycap (e.g., \"Ctrl + A\" instead of \"Ctrl A\" or \" + A\" instead of \" A\")")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSpinBox {
|
||||||
|
text: Translation.tr("Keybind font size")
|
||||||
|
value: Config.options.cheatsheet.fontSize.key
|
||||||
|
from: 8
|
||||||
|
to: 30
|
||||||
|
stepSize: 1
|
||||||
|
onValueChanged: {
|
||||||
|
Config.options.cheatsheet.fontSize.key = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConfigSpinBox {
|
||||||
|
text: Translation.tr("Description font size")
|
||||||
|
value: Config.options.cheatsheet.fontSize.comment
|
||||||
|
from: 8
|
||||||
|
to: 30
|
||||||
|
stepSize: 1
|
||||||
|
onValueChanged: {
|
||||||
|
Config.options.cheatsheet.fontSize.comment = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ContentSection {
|
ContentSection {
|
||||||
icon: "call_to_action"
|
icon: "call_to_action"
|
||||||
title: Translation.tr("Dock")
|
title: Translation.tr("Dock")
|
||||||
@@ -647,4 +739,5 @@ ContentPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import QtQuick.Layouts
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
property int currentTab: 0
|
|
||||||
property var tabButtonList: [
|
property var tabButtonList: [
|
||||||
{"name": Translation.tr("Pomodoro"), "icon": "search_activity"},
|
{"name": Translation.tr("Pomodoro"), "icon": "search_activity"},
|
||||||
{"name": Translation.tr("Stopwatch"), "icon": "timer"}
|
{"name": Translation.tr("Stopwatch"), "icon": "timer"}
|
||||||
@@ -17,20 +16,20 @@ Item {
|
|||||||
Keys.onPressed: (event) => {
|
Keys.onPressed: (event) => {
|
||||||
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) { // Switch tabs
|
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) { // Switch tabs
|
||||||
if (event.key === Qt.Key_PageDown) {
|
if (event.key === Qt.Key_PageDown) {
|
||||||
currentTab = Math.min(currentTab + 1, root.tabButtonList.length - 1)
|
tabBar.incrementCurrentIndex();
|
||||||
} else if (event.key === Qt.Key_PageUp) {
|
} else if (event.key === Qt.Key_PageUp) {
|
||||||
currentTab = Math.max(currentTab - 1, 0)
|
tabBar.decrementCurrentIndex();
|
||||||
}
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) { // Pause/resume with Space or S
|
} else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) { // Pause/resume with Space or S
|
||||||
if (currentTab === 0) {
|
if (tabBar.currentIndex === 0) {
|
||||||
TimerService.togglePomodoro()
|
TimerService.togglePomodoro()
|
||||||
} else {
|
} else {
|
||||||
TimerService.toggleStopwatch()
|
TimerService.toggleStopwatch()
|
||||||
}
|
}
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_R) { // Reset with R
|
} else if (event.key === Qt.Key_R) { // Reset with R
|
||||||
if (currentTab === 0) {
|
if (tabBar.currentIndex === 0) {
|
||||||
TimerService.resetPomodoro()
|
TimerService.resetPomodoro()
|
||||||
} else {
|
} else {
|
||||||
TimerService.stopwatchReset()
|
TimerService.stopwatchReset()
|
||||||
@@ -46,82 +45,19 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
TabBar {
|
SecondaryTabBar {
|
||||||
id: tabBar
|
id: tabBar
|
||||||
Layout.fillWidth: true
|
currentIndex: swipeView.currentIndex
|
||||||
currentIndex: currentTab
|
|
||||||
onCurrentIndexChanged: currentTab = currentIndex
|
|
||||||
|
|
||||||
background: Item {
|
|
||||||
WheelHandler {
|
|
||||||
onWheel: (event) => {
|
|
||||||
if (event.angleDelta.y < 0)
|
|
||||||
tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1)
|
|
||||||
else if (event.angleDelta.y > 0)
|
|
||||||
tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0)
|
|
||||||
}
|
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.tabButtonList
|
model: root.tabButtonList
|
||||||
delegate: SecondaryTabButton {
|
delegate: SecondaryTabButton {
|
||||||
selected: (index == currentTab)
|
|
||||||
buttonText: modelData.name
|
buttonText: modelData.name
|
||||||
buttonIcon: modelData.icon
|
buttonIcon: modelData.icon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { // Tab indicator
|
|
||||||
id: tabIndicator
|
|
||||||
Layout.fillWidth: true
|
|
||||||
height: 3
|
|
||||||
property bool enableIndicatorAnimation: false
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onCurrentTabChanged() {
|
|
||||||
tabIndicator.enableIndicatorAnimation = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: indicator
|
|
||||||
property int tabCount: root.tabButtonList.length
|
|
||||||
property real fullTabSize: root.width / tabCount;
|
|
||||||
property real targetWidth: tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth
|
|
||||||
|
|
||||||
implicitWidth: targetWidth
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
bottom: parent.bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2
|
|
||||||
|
|
||||||
color: Appearance.colors.colPrimary
|
|
||||||
radius: Appearance.rounding.full
|
|
||||||
|
|
||||||
Behavior on x {
|
|
||||||
enabled: tabIndicator.enableIndicatorAnimation
|
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
|
||||||
enabled: tabIndicator.enableIndicatorAnimation
|
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle { // Tabbar bottom border
|
|
||||||
id: tabBarBottomBorder
|
|
||||||
Layout.fillWidth: true
|
|
||||||
height: 1
|
|
||||||
color: Appearance.colors.colOutlineVariant
|
|
||||||
}
|
|
||||||
|
|
||||||
SwipeView {
|
SwipeView {
|
||||||
id: swipeView
|
id: swipeView
|
||||||
Layout.topMargin: 10
|
Layout.topMargin: 10
|
||||||
@@ -129,11 +65,7 @@ Item {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
spacing: 10
|
spacing: 10
|
||||||
clip: true
|
clip: true
|
||||||
currentIndex: currentTab
|
currentIndex: tabBar.currentIndex
|
||||||
onCurrentIndexChanged: {
|
|
||||||
tabIndicator.enableIndicatorAnimation = true
|
|
||||||
currentTab = currentIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tabs
|
// Tabs
|
||||||
PomodoroTimer {}
|
PomodoroTimer {}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import QtQuick.Layouts
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
property int currentTab: 0
|
|
||||||
property var tabButtonList: [{"icon": "checklist", "name": Translation.tr("Unfinished")}, {"name": Translation.tr("Done"), "icon": "check_circle"}]
|
property var tabButtonList: [{"icon": "checklist", "name": Translation.tr("Unfinished")}, {"name": Translation.tr("Done"), "icon": "check_circle"}]
|
||||||
property bool showAddDialog: false
|
property bool showAddDialog: false
|
||||||
property int dialogMargins: 20
|
property int dialogMargins: 20
|
||||||
@@ -17,9 +16,9 @@ Item {
|
|||||||
Keys.onPressed: (event) => {
|
Keys.onPressed: (event) => {
|
||||||
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) {
|
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) {
|
||||||
if (event.key === Qt.Key_PageDown) {
|
if (event.key === Qt.Key_PageDown) {
|
||||||
currentTab = Math.min(currentTab + 1, root.tabButtonList.length - 1)
|
tabBar.incrementCurrentIndex();
|
||||||
} else if (event.key === Qt.Key_PageUp) {
|
} else if (event.key === Qt.Key_PageUp) {
|
||||||
currentTab = Math.max(currentTab - 1, 0)
|
tabBar.decrementCurrentIndex();
|
||||||
}
|
}
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
@@ -39,82 +38,19 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
TabBar {
|
SecondaryTabBar {
|
||||||
id: tabBar
|
id: tabBar
|
||||||
Layout.fillWidth: true
|
currentIndex: swipeView.currentIndex
|
||||||
currentIndex: currentTab
|
|
||||||
onCurrentIndexChanged: currentTab = currentIndex
|
|
||||||
|
|
||||||
background: Item {
|
|
||||||
WheelHandler {
|
|
||||||
onWheel: (event) => {
|
|
||||||
if (event.angleDelta.y < 0)
|
|
||||||
tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1)
|
|
||||||
else if (event.angleDelta.y > 0)
|
|
||||||
tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0)
|
|
||||||
}
|
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.tabButtonList
|
model: root.tabButtonList
|
||||||
delegate: SecondaryTabButton {
|
delegate: SecondaryTabButton {
|
||||||
selected: (index == currentTab)
|
|
||||||
buttonText: modelData.name
|
buttonText: modelData.name
|
||||||
buttonIcon: modelData.icon
|
buttonIcon: modelData.icon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { // Tab indicator
|
|
||||||
id: tabIndicator
|
|
||||||
Layout.fillWidth: true
|
|
||||||
height: 3
|
|
||||||
property bool enableIndicatorAnimation: false
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onCurrentTabChanged() {
|
|
||||||
tabIndicator.enableIndicatorAnimation = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: indicator
|
|
||||||
property int tabCount: root.tabButtonList.length
|
|
||||||
property real fullTabSize: root.width / tabCount;
|
|
||||||
property real targetWidth: tabBar?.contentItem?.children[0]?.children[tabBar.currentIndex]?.tabContentWidth ?? 0
|
|
||||||
|
|
||||||
implicitWidth: targetWidth
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
bottom: parent.bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2
|
|
||||||
|
|
||||||
color: Appearance.colors.colPrimary
|
|
||||||
radius: Appearance.rounding.full
|
|
||||||
|
|
||||||
Behavior on x {
|
|
||||||
enabled: tabIndicator.enableIndicatorAnimation
|
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
|
||||||
enabled: tabIndicator.enableIndicatorAnimation
|
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle { // Tabbar bottom border
|
|
||||||
id: tabBarBottomBorder
|
|
||||||
Layout.fillWidth: true
|
|
||||||
height: 1
|
|
||||||
color: Appearance.colors.colOutlineVariant
|
|
||||||
}
|
|
||||||
|
|
||||||
SwipeView {
|
SwipeView {
|
||||||
id: swipeView
|
id: swipeView
|
||||||
Layout.topMargin: 10
|
Layout.topMargin: 10
|
||||||
@@ -122,11 +58,7 @@ Item {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
spacing: 10
|
spacing: 10
|
||||||
clip: true
|
clip: true
|
||||||
currentIndex: currentTab
|
currentIndex: tabBar.currentIndex
|
||||||
onCurrentIndexChanged: {
|
|
||||||
tabIndicator.enableIndicatorAnimation = true
|
|
||||||
currentTab = currentIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
// To Do tab
|
// To Do tab
|
||||||
TaskList {
|
TaskList {
|
||||||
@@ -215,7 +147,7 @@ Item {
|
|||||||
Todo.addTask(todoInput.text)
|
Todo.addTask(todoInput.text)
|
||||||
todoInput.text = ""
|
todoInput.text = ""
|
||||||
root.showAddDialog = false
|
root.showAddDialog = false
|
||||||
root.currentTab = 0 // Show unfinished tasks
|
tabBar.setCurrentIndex(0) // Show unfinished tasks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ ColumnLayout {
|
|||||||
Layout.topMargin: -22
|
Layout.topMargin: -22
|
||||||
Layout.leftMargin: 0
|
Layout.leftMargin: 0
|
||||||
Layout.rightMargin: 0
|
Layout.rightMargin: 0
|
||||||
|
color: Appearance.colors.colOutlineVariant
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogSectionListView {
|
DialogSectionListView {
|
||||||
@@ -56,6 +57,7 @@ ColumnLayout {
|
|||||||
Layout.topMargin: -22
|
Layout.topMargin: -22
|
||||||
Layout.leftMargin: 0
|
Layout.leftMargin: 0
|
||||||
Layout.rightMargin: 0
|
Layout.rightMargin: 0
|
||||||
|
color: Appearance.colors.colOutlineVariant
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogSectionListView {
|
DialogSectionListView {
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ Item {
|
|||||||
sourceSize.height: size
|
sourceSize.height: size
|
||||||
source: {
|
source: {
|
||||||
let icon;
|
let icon;
|
||||||
icon = AppSearch.guessIcon(root.node.properties["application.icon-name"]);
|
icon = AppSearch.guessIcon(root.node?.properties["application.icon-name"] ?? "");
|
||||||
if (AppSearch.iconExists(icon))
|
if (AppSearch.iconExists(icon))
|
||||||
return Quickshell.iconPath(icon, "image-missing");
|
return Quickshell.iconPath(icon, "image-missing");
|
||||||
icon = AppSearch.guessIcon(root.node.properties["node.name"]);
|
icon = AppSearch.guessIcon(root.node?.properties["node.name"] ?? "");
|
||||||
return Quickshell.iconPath(icon, "image-missing");
|
return Quickshell.iconPath(icon, "image-missing");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ Item {
|
|||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
text: {
|
text: {
|
||||||
// application.name -> description -> name
|
// application.name -> description -> name
|
||||||
const app = root.node.properties["application.name"] ?? (root.node.description != "" ? root.node.description : root.node.name);
|
const app = root.node?.properties["application.name"] ?? (root.node.description != "" ? root.node.description : root.node.name);
|
||||||
const media = root.node.properties["media.name"];
|
const media = root.node.properties["media.name"];
|
||||||
return media != undefined ? `${app} • ${media}` : app;
|
return media != undefined ? `${app} • ${media}` : app;
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ Item {
|
|||||||
|
|
||||||
StyledSlider {
|
StyledSlider {
|
||||||
id: slider
|
id: slider
|
||||||
value: root.node.audio.volume
|
value: root.node?.audio.volume ?? 0
|
||||||
onMoved: root.node.audio.volume = value
|
onMoved: root.node.audio.volume = value
|
||||||
configuration: StyledSlider.Configuration.S
|
configuration: StyledSlider.Configuration.S
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,17 +10,55 @@ import Quickshell.Io
|
|||||||
* Simple polled resource usage service with RAM, Swap, and CPU usage.
|
* Simple polled resource usage service with RAM, Swap, and CPU usage.
|
||||||
*/
|
*/
|
||||||
Singleton {
|
Singleton {
|
||||||
property double memoryTotal: 1
|
id: root
|
||||||
property double memoryFree: 1
|
property real memoryTotal: 1
|
||||||
property double memoryUsed: memoryTotal - memoryFree
|
property real memoryFree: 0
|
||||||
property double memoryUsedPercentage: memoryUsed / memoryTotal
|
property real memoryUsed: memoryTotal - memoryFree
|
||||||
property double swapTotal: 1
|
property real memoryUsedPercentage: memoryUsed / memoryTotal
|
||||||
property double swapFree: 1
|
property real swapTotal: 1
|
||||||
property double swapUsed: swapTotal - swapFree
|
property real swapFree: 0
|
||||||
property double swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0
|
property real swapUsed: swapTotal - swapFree
|
||||||
property double cpuUsage: 0
|
property real swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0
|
||||||
|
property real cpuUsage: 0
|
||||||
property var previousCpuStats
|
property var previousCpuStats
|
||||||
|
|
||||||
|
property string maxAvailableMemoryString: kbToGbString(ResourceUsage.memoryTotal)
|
||||||
|
property string maxAvailableSwapString: kbToGbString(ResourceUsage.swapTotal)
|
||||||
|
property string maxAvailableCpuString: "--"
|
||||||
|
|
||||||
|
readonly property int historyLength: Config?.options.resources.historyLength ?? 60
|
||||||
|
property list<real> cpuUsageHistory: []
|
||||||
|
property list<real> memoryUsageHistory: []
|
||||||
|
property list<real> swapUsageHistory: []
|
||||||
|
|
||||||
|
function kbToGbString(kb) {
|
||||||
|
return (kb / (1024 * 1024)).toFixed(1) + " GB";
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMemoryUsageHistory() {
|
||||||
|
memoryUsageHistory = [...memoryUsageHistory, memoryUsedPercentage]
|
||||||
|
if (memoryUsageHistory.length > historyLength) {
|
||||||
|
memoryUsageHistory.shift()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateSwapUsageHistory() {
|
||||||
|
swapUsageHistory = [...swapUsageHistory, swapUsedPercentage]
|
||||||
|
if (swapUsageHistory.length > historyLength) {
|
||||||
|
swapUsageHistory.shift()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateCpuUsageHistory() {
|
||||||
|
cpuUsageHistory = [...cpuUsageHistory, cpuUsage]
|
||||||
|
if (cpuUsageHistory.length > historyLength) {
|
||||||
|
cpuUsageHistory.shift()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateHistories() {
|
||||||
|
updateMemoryUsageHistory()
|
||||||
|
updateSwapUsageHistory()
|
||||||
|
updateCpuUsageHistory()
|
||||||
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
interval: 1
|
interval: 1
|
||||||
running: true
|
running: true
|
||||||
@@ -53,10 +91,24 @@ Singleton {
|
|||||||
|
|
||||||
previousCpuStats = { total, idle }
|
previousCpuStats = { total, idle }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
root.updateHistories()
|
||||||
interval = Config.options?.resources?.updateInterval ?? 3000
|
interval = Config.options?.resources?.updateInterval ?? 3000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileView { id: fileMeminfo; path: "/proc/meminfo" }
|
FileView { id: fileMeminfo; path: "/proc/meminfo" }
|
||||||
FileView { id: fileStat; path: "/proc/stat" }
|
FileView { id: fileStat; path: "/proc/stat" }
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: findCpuMaxFreqProc
|
||||||
|
command: ["bash", "-c", "lscpu | grep 'CPU max MHz' | awk '{print $4}'"]
|
||||||
|
running: true
|
||||||
|
stdout: StdioCollector {
|
||||||
|
id: outputCollector
|
||||||
|
onStreamFinished: {
|
||||||
|
root.maxAvailableCpuString = (parseFloat(outputCollector.text) / 1000).toFixed(0) + " GHz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user