Files
Dotfiles/noctalia/plugins/catwalk/BarWidget.qml
T
2026-04-19 17:07:18 +02:00

194 lines
6.5 KiB
QML

import QtQuick
import QtQuick.Effects
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Modules.Bar.Extras
import qs.Services.UI
import qs.Widgets
import qs.Services.System
Item {
id: root
property var pluginApi: null
property ShellScreen screen
property string widgetId: ""
property string section: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
// Per-screen bar properties
readonly property string screenName: screen?.name ?? ""
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
property url currentIconSource
property string tooltipText: {
if (!pluginApi) return "";
return root.isRunning ? (pluginApi.tr("tooltip.running") || "Running") : (pluginApi.tr("tooltip.sleeping") || "Sleeping");
}
property string tooltipDirection: BarService.getTooltipDirection()
property bool enabled: true
property bool allowClickWhenDisabled: false
property bool hovering: false
property color colorBg: Color.mSurfaceVariant
property color colorFg: Color.mPrimary
property color colorBgHover: Color.mHover
property color colorFgHover: Color.mOnHover
property color colorBorder: Color.mOutline
property color colorBorderHover: Color.mOutline
property real customRadius: Style.radiusL
signal entered
signal exited
signal clicked
signal rightClicked
signal middleClicked
signal wheel(int angleDelta)
readonly property real contentWidth: barIsVertical ? capsuleHeight : Math.round(capsuleHeight + Style.marginXS * 2)
readonly property real contentHeight: capsuleHeight
implicitWidth: contentWidth
implicitHeight: contentHeight
// --- Catwalk Specific Logic ---
property int frameIndex: 0
property int idleFrameIndex: 0
readonly property bool isRunning: root.pluginApi?.mainInstance?.isRunning ?? false
readonly property var icons: root.pluginApi?.mainInstance?.icons || []
readonly property var idleIcons: root.pluginApi?.mainInstance?.idleIcons || []
readonly property real cpuUsage: root.pluginApi?.mainInstance?.cpuUsage ?? 0
function openPanel() {
if (pluginApi) {
var result = pluginApi.openPanel(root.screen);
Logger.i("Catwalk", "OpenPanel result:", result);
} else {
Logger.e("Catwalk", "PluginAPI is null");
}
}
function openExternalMonitor() {
Quickshell.execDetached(["sh", "-c", Settings.data.systemMonitor.externalMonitor]);
}
Timer {
interval: Math.max(30, 200 - root.cpuUsage * 1.7)
running: root.isRunning
repeat: true
onTriggered: {
root.frameIndex = (root.frameIndex + 1) % root.icons.length
}
}
Timer {
interval: 400
running: !root.isRunning
repeat: true
onTriggered: {
root.idleFrameIndex = (root.idleFrameIndex + 1) % root.idleIcons.length
}
}
currentIconSource: (root.icons && root.icons.length > 0 && root.idleIcons && root.idleIcons.length > 0)
? (root.isRunning
? Qt.resolvedUrl(root.icons[root.frameIndex % root.icons.length])
: Qt.resolvedUrl(root.idleIcons[root.idleFrameIndex % root.idleIcons.length]))
: ""
Rectangle {
id: visualCapsule
x: Style.pixelAlignCenter(parent.width, width)
y: Style.pixelAlignCenter(parent.height, height)
width: root.contentWidth
height: root.contentHeight
opacity: root.enabled ? Style.opacityFull : Style.opacityMedium
color: mouseArea.containsMouse ? Color.mHover : Style.capsuleColor
radius: Math.min((customRadius >= 0 ? customRadius : Style.iRadiusL), width / 2)
border.color: Style.capsuleBorderColor
border.width: Style.capsuleBorderWidth
Behavior on color {
ColorAnimation {
duration: Style.animationNormal
easing.type: Easing.InOutQuad
}
}
Image {
id: iconImage
source: root.currentIconSource
x: Style.pixelAlignCenter(parent.width, width)
y: Style.pixelAlignCenter(parent.height, height)
width: Style.toOdd(visualCapsule.width - Style.marginXS * 2)
height: width
// Render SVG at exact target size for crisp output
sourceSize: Qt.size(width, height)
fillMode: Image.PreserveAspectFit
smooth: true
mipmap: false
// This enables the "mask" behavior to recolor the icon
layer.enabled: true
layer.effect: MultiEffect {
colorization: 1.0
colorizationColor: Settings.data.colorSchemes.darkMode ? "white" : "black"
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
hoverEnabled: true
onEntered: {
root.hovering = true;
if (root.tooltipText) {
TooltipService.show(root, root.tooltipText, root.tooltipDirection);
}
root.entered();
}
onExited: {
root.hovering = false;
if (root.tooltipText) {
TooltipService.hide();
}
root.exited();
}
onClicked: function (mouse) {
if (root.tooltipText) {
TooltipService.hide();
}
Logger.i("Catwalk", "Clicked! API:", !!pluginApi, "Screen:", root.screen ? root.screen.name : "null");
if (!root.enabled && !root.allowClickWhenDisabled) {
return;
}
// Open Panel on left/right click
// Open external monitor on middle click
if (mouse.button === Qt.LeftButton) {
root.openPanel();
root.clicked();
} else if (mouse.button === Qt.RightButton) {
root.openPanel();
root.rightClicked();
} else if (mouse.button === Qt.MiddleButton) {
root.openExternalMonitor();
root.middleClicked();
}
}
onWheel: wheel => root.wheel(wheel.angleDelta.y)
}
}