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

249 lines
10 KiB
QML

import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Widgets
import qs.Services.UI
Item {
id: root
property var pluginApi: null
property ShellScreen screen
property string widgetId: ""
property string section: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
readonly property var mainInstance: pluginApi?.mainInstance
readonly property string screenName: screen?.name ?? ""
readonly property string resolvedBarPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isBarVertical: resolvedBarPosition === "left" || resolvedBarPosition === "right"
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
readonly property real barFontSize: Style.getBarFontSizeForScreen(screenName)
// Settings tie-ins
readonly property real catSize: mainInstance?.catSize ?? 1.0
readonly property real catOffsetY: mainInstance?.catOffsetY ?? 0.0
readonly property real widthPadding: pluginApi?.pluginSettings?.widthPadding ?? 0.2
// Orientation-aware cat sizing (both driven by the catSize slider)
readonly property real catSizeHorizontal: catSize
readonly property real catSizeVertical: catSize * 0.50
readonly property real activeCatSize: isBarVertical ? catSizeVertical : catSizeHorizontal
// Glyph map: b = left paw up, d = left paw down, c = right paw up, a = right paw down, e+f = sleep, g+h = blink
readonly property var glyphMap: ["bc", "dc", "ba", "da"] // [idle, leftSlap, rightSlap, bothSlap]
readonly property string sleepGlyph: "ef"
readonly property string blinkGlyph: "gh"
readonly property int catState: mainInstance?.catState ?? 0
readonly property bool paused: mainInstance?.paused ?? false
readonly property bool waiting: mainInstance?.waiting ?? false
readonly property string catColorKey: mainInstance?.catColor ?? "default"
readonly property bool blinking: mainInstance?.blinking ?? false
readonly property bool showRainbowColor: mainInstance?.showRainbowColor ?? false
readonly property string rainbowColor: mainInstance?.currentRainbowColor ?? "#ff0000"
function resolveColor(key) {
switch (key) {
case "primary": return Color.mPrimary
case "secondary": return Color.mSecondary
case "tertiary": return Color.mTertiary
case "error": return Color.mError
default: return Color.mOnSurface
}
}
readonly property color resolvedCatColor: showRainbowColor ? rainbowColor : resolveColor(catColorKey)
// Sizing: capsule dimensions drive implicit size
readonly property real horizontalPadding: capsuleHeight * widthPadding
readonly property real contentWidth: isBarVertical
? capsuleHeight
: catText.implicitWidth + horizontalPadding + (paused ? pauseExpandAmount : 0)
readonly property real contentHeight: isBarVertical
? catText.implicitHeight + horizontalPadding + (paused ? pauseExpandAmount : 0)
: capsuleHeight
// Pause indicator expand/slide
readonly property real pauseIconSize: capsuleHeight * 0.45
readonly property real pauseExpandAmount: capsuleHeight * 0.8
readonly property real pauseSlideOffset: paused ? -pauseExpandAmount / 2 : 0
implicitWidth: contentWidth
implicitHeight: contentHeight
FontLoader {
id: bongoFont
source: pluginApi ? pluginApi.pluginDir + "/bongocat-Regular.otf" : ""
}
Rectangle {
id: visualCapsule
x: Style.pixelAlignCenter(parent.width, width)
y: Style.pixelAlignCenter(parent.height, height)
width: root.contentWidth
height: root.contentHeight
radius: Style.radiusL
Behavior on width {
NumberAnimation { duration: Style.animationNormal; easing.type: Easing.OutCubic }
}
Behavior on height {
NumberAnimation { duration: Style.animationNormal; easing.type: Easing.OutCubic }
}
color: mouseArea.containsMouse ? Color.mHover : (root.paused ? root.resolvedCatColor : Style.capsuleColor)
border.color: Style.capsuleBorderColor
border.width: Style.capsuleBorderWidth
Behavior on color {
ColorAnimation { duration: Style.animationNormal; easing.type: Easing.OutCubic }
}
Text {
id: catText
anchors.centerIn: parent
anchors.horizontalCenterOffset: root.isBarVertical ? 0 : root.pauseSlideOffset
anchors.verticalCenterOffset: root.capsuleHeight * root.catOffsetY + (root.isBarVertical ? root.pauseSlideOffset : 0)
font.family: bongoFont.name
font.pixelSize: root.capsuleHeight * root.activeCatSize
font.weight: Font.Thin
color: mouseArea.containsMouse ? Color.mOnHover : (root.paused ? Color.mSurface : root.resolvedCatColor)
text: (root.paused || root.waiting) ? root.sleepGlyph : (root.blinking ? root.blinkGlyph : (root.glyphMap[root.catState] ?? "bc"))
Behavior on anchors.horizontalCenterOffset {
NumberAnimation { duration: Style.animationNormal; easing.type: Easing.OutCubic }
}
Behavior on anchors.verticalCenterOffset {
NumberAnimation { duration: Style.animationNormal; easing.type: Easing.OutCubic }
}
}
NIcon {
id: pauseIcon
icon: "player-pause-filled"
pointSize: root.pauseIconSize
applyUiScale: false
color: catText.color
opacity: root.paused ? 1 : 0
x: root.isBarVertical
? (parent.width - width) / 2
: parent.width - (root.pauseExpandAmount + width) / 2 - root.capsuleHeight * 0.20
y: root.isBarVertical
? parent.height - (root.pauseExpandAmount + height) / 2 - root.capsuleHeight * 0.10
: (parent.height - height) / 2
Behavior on opacity {
NumberAnimation { duration: Style.animationFast; easing.type: Easing.OutCubic }
}
}
Repeater {
id: zzzRepeater
property real catFontSize: catText.font.pixelSize
property color catFontColor: catText.color
property real catX: catText.x
property real catY: catText.y
property real catW: catText.width
property bool sleeping: root.paused || root.waiting
readonly property real baseScale: 0.28
readonly property real scaleStep: 0.07
readonly property real xOrigin: 0.55
readonly property real xSpacing: 0.18
readonly property real floatHeight: 0.7
readonly property int staggerDelay: 500
readonly property int floatDuration: 1800
readonly property int fadeInDuration: 300
readonly property int fadeOutDuration: 1500
model: 3
delegate: Text {
id: zItem
required property int index
text: "z"
font.pixelSize: zzzRepeater.catFontSize * (zzzRepeater.baseScale + index * zzzRepeater.scaleStep)
font.weight: Font.Bold
color: zzzRepeater.catFontColor
visible: zzzRepeater.sleeping
opacity: 0
x: zzzRepeater.catX + zzzRepeater.catW * zzzRepeater.xOrigin + index * zzzRepeater.catFontSize * zzzRepeater.xSpacing
y: zzzRepeater.catY
SequentialAnimation {
id: zAnim
running: zzzRepeater.sleeping
loops: Animation.Infinite
PauseAnimation { duration: zItem.index * zzzRepeater.staggerDelay }
ParallelAnimation {
NumberAnimation {
target: zItem; property: "y"
from: zzzRepeater.catY
to: zzzRepeater.catY - zzzRepeater.catFontSize * zzzRepeater.floatHeight
duration: zzzRepeater.floatDuration
easing.type: Easing.OutQuad
}
SequentialAnimation {
NumberAnimation {
target: zItem; property: "opacity"
from: 0; to: 1
duration: zzzRepeater.fadeInDuration
}
NumberAnimation {
target: zItem; property: "opacity"
from: 1; to: 0
duration: zzzRepeater.fadeOutDuration
easing.type: Easing.InQuad
}
}
}
}
onVisibleChanged: {
if (!visible) {
zAnim.stop();
opacity = 0;
y = zzzRepeater.catY;
}
}
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.PointingHandCursor
onClicked: mouse => {
if (mouse.button === Qt.RightButton) {
PanelService.showContextMenu(contextMenu, root, screen);
} else if (root.mainInstance) {
root.mainInstance.paused = !root.mainInstance.paused;
}
}
}
NPopupContextMenu {
id: contextMenu
model: [{
"label": I18n.tr("actions.widget-settings"),
"action": "widget-settings",
"icon": "settings"
}]
onTriggered: action => {
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "widget-settings") {
BarService.openPluginSettings(screen, pluginApi.manifest);
}
}
}
}