This commit is contained in:
2026-06-13 20:53:00 +01:00
parent 1356456ab0
commit 6e84d0ca21
11 changed files with 2 additions and 730 deletions

56
Bar.qml
View File

@@ -4,8 +4,6 @@ import Quickshell
import Quickshell.Hyprland
import "Components/Color.js" as Colors
import "Components"
Scope {
Variants {
model: Quickshell.screens
@@ -19,67 +17,17 @@ Scope {
color: "transparent"
exclusionMode: ExclusionMode.Normal
exclusiveZone: 20
exclusiveZone: 15
implicitHeight: 1000
screen: modelData
Behavior on implicitHeight {
NumberAnimation {
duration: 0
easing.type: Easing.InOutQuad
}
}
mask: Region {
Region {
item: workspaceSwitcher
}
Region {
item: pillItem
}
Region {
item: utilsItem
}
}
mask: Region {}
anchors {
left: true
right: true
top: true
}
Workspace {
id: workspaceSwitcher
monitor: win.monitor
}
RectangularShadow {
anchors.fill: pillItem
blur: 20
color: Colors.crust
offset.x: 3
offset.y: 3
radius: pillItem.radius
spread: 7
}
Pill {
id: pillItem
monitor: win.monitor
}
Item {
anchors.fill: parent
Utils {
id: utilsItem
monitor: win.monitor
}
}
}
}
}

View File

@@ -1,42 +0,0 @@
// Time.qml
// with this line our type becomes a Singleton
pragma Singleton
import Quickshell
import Quickshell.Io
import QtQuick
// your singletons should always have Singleton as the type
Singleton {
id: root
property string time
property string day
Process {
id: timeProc
command: ["date", "+%T"]
running: true
stdout: StdioCollector {
onStreamFinished: root.time = this.text
}
}
Process {
id: dateProc
command: ["date", "+%A %B %d %Y"]
running: true
stdout: StdioCollector {
onStreamFinished: root.day = this.text
}
}
Timer {
interval: 500
running: true
repeat: true
onTriggered: timeProc.running = true, dateProc.running = true
}
}

View File

@@ -1,140 +0,0 @@
import Quickshell
import Quickshell.Hyprland
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import "Color.js" as Colors
import "./Pill"
Rectangle {
required property HyprlandMonitor monitor
id: container
state: ""
visible: true
property bool shown: monitor !== null && monitor.focused
Behavior on anchors.topMargin {
SmoothedAnimation { duration: 70; easing.type: Easing.OutBounce }
}
opacity: shown ? 1 : 0
Behavior on opacity {
NumberAnimation {duration: 50}
}
anchors {
top: parent.top
topMargin: shown ? 5 : -implicitHeight
horizontalCenter: parent.horizontalCenter
}
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: container.state == 'expanded' ? container.state = "" : container.state = 'expanded';
}
implicitWidth: 130
implicitHeight: 30
radius: 30
property real gradientMidpoint: 0.4
gradient: Gradient {
GradientStop { position: 0.0; color: Colors.surface0 }
GradientStop { position: container.gradientMidpoint; color: Colors.surface0 }
GradientStop { position: 1.0; color: Colors.surface2 }
}
border {color: Colors.lavender; width: 2}
component SmoothAnim: SpringAnimation {
spring: 3
mass: 0.5
damping: 0.18
}
Behavior on implicitHeight {
SmoothAnim {}
}
Behavior on implicitWidth {
SmoothAnim {}
}
Behavior on gradientMidpoint {
NumberAnimation {
duration: 100
}
}
states: [
State {
name: ""
StateChangeScript {
script: mainLoader.replace(defaultComponent)
}
},
State {
name: "expanded"
StateChangeScript {
script: mainLoader.replace(expandedComponent)
}
PropertyChanges { container.gradientMidpoint: 0.7}
PropertyChanges { container.implicitHeight: 130}
PropertyChanges { container.implicitWidth: 300}
}
]
StackView {
id: mainLoader
anchors.fill: parent
MouseArea {
id: mouseAreaStack
anchors.fill: parent
hoverEnabled: true
// FIX part 1: Prevent internal StackView items from changing cursor grab
propagateComposedEvents: true
onClicked: {
container.state == 'expanded' ? container.state = "" : container.state = 'expanded';
}
onExited: {
container.state = ""
}
}
enabled:false
initialItem: defaultComponent
// Custom animations for switching components
replaceEnter: Transition {
PropertyAnimation { property: "opacity"; from: 0; to: 1; duration: 100 }
PropertyAnimation { property: "scale"; from: 0.9; to: 1.0; duration: 100 }
}
replaceExit: Transition {
PropertyAnimation { property: "opacity"; from: 1; to: 0; duration: 100 }
}
}
Component {
id: defaultComponent
Default {}
}
Component {
id: expandedComponent
Expanded {}
}
}

View File

@@ -1,16 +0,0 @@
import Quickshell
import Quickshell.Hyprland
import QtQuick
import QtQuick.Controls
import "../Color.js" as Colors
import "../"
Item {
Text {
font.family: "FiraMono Nerd Font"
anchors.centerIn: parent
font.pixelSize: 15
color: Colors.text
text: Clock.time
}
}

View File

@@ -1,29 +0,0 @@
import Quickshell
import Quickshell.Hyprland
import QtQuick
import QtQuick.Layouts
import "../Color.js" as Colors
import "../"
Item {
anchors.fill: parent
Column {
spacing: 5
anchors.centerIn: parent
Text {
font.family: "FiraMono Nerd Font"
font.pixelSize: 40
color: Colors.text
anchors.horizontalCenter: parent.horizontalCenter
text: Clock.time
}
Text {
font.family: "FiraMono Nerd Font"
color: Colors.subtext0
anchors.horizontalCenter: parent.horizontalCenter
text: Clock.day
}
}
}

View File

@@ -1,45 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import "./Utils"
RowLayout {
id: utilsContainer
required property HyprlandMonitor monitor
property bool shown: monitor !== null && monitor.focused
opacity: shown ? 1 : 0
state: ""
Behavior on anchors.topMargin {
SmoothedAnimation {
duration: 300
}
}
Behavior on opacity {
NumberAnimation {
duration: 50
}
}
states: [
State {
name: ""
}
]
anchors {
right: parent.right
rightMargin: 7
top: parent.top
topMargin: shown ? -33 : -40
}
Battery {}
// Mpris {}
}

View File

@@ -1,46 +0,0 @@
import QtQuick
import Quickshell
import Quickshell.Services.UPower
import "../Color.js" as Colors
import "./"
Container {
id: batteryRoot
boxColor: Colors.green
boxState: ""
sourceComponent: batteryClosed
visible: UPower.devices[0] ? true : false
MouseArea {
anchors.fill: parent
enabled: true
hoverEnabled: true
propagateComposedEvents: true
onClicked: batteryRoot.boxState == "" ? batteryRoot.boxState = "open" : batteryRoot.boxState = ""
onExited: batteryRoot.boxState = ""
}
Component {
id: batteryClosed
Item {
id: batteryRoot
anchors.fill: parent
Text {
color: Colors.base
text: "something"
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
}
}
}
}

View File

@@ -1,64 +0,0 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
Item {
id: container
required property color boxColor
property double boxHeight
property double boxRadius
required property string boxState
property double boxWidth
property double openHeight
property double openWidth
required property Component sourceComponent
Layout.topMargin: 0
implicitHeight: boxHeight || 50
implicitWidth: boxWidth || 30
state: boxState
Behavior on anchors.topMargin {
SmoothAnim {}
}
Behavior on implicitHeight {
SmoothAnim {}
}
Behavior on implicitWidth {
SmoothAnim {}
}
states: [
State {
name: ""
},
State {
name: "open"
PropertyChanges {
container.Layout.topMargin: 35
container.implicitHeight: openHeight || 150
container.implicitWidth: openWidth || 200
}
}
]
Rectangle {
id: mainBackground
anchors.fill: parent
color: container.boxColor
radius: container.boxRadius || 10
Loader {
anchors.fill: parent
sourceComponent: container.sourceComponent
}
}
component SmoothAnim: SpringAnimation {
damping: 0.18
mass: 0.5
spring: 3
}
}

View File

@@ -1,4 +0,0 @@
import QtQuick
import Quickshell
Item {}

View File

@@ -1,290 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import "Color.js" as Colors
Item {
id: workspaceRowContainer
required property HyprlandMonitor monitor
property int textBottomMargin: 0
function getNextEmptyWorkspaceId() {
let maxId = 0;
let workspaces = Hyprland.workspaces.values;
for (let i = 0; i < workspaces.length; i++) {
let ws = workspaces[i];
// Look at all numerical workspaces globally (ignoring special workspaces like scratchpads)
if (ws.id > maxId && ws.id < 99) {
maxId = ws.id;
}
}
// Returns the next global workspace ID (e.g., if max is 3, returns 4)
console.log(maxId + 1);
return maxId === 0 ? 1 : maxId + 1;
}
implicitHeight: 65
implicitWidth: 200
state: ""
Behavior on anchors.topMargin {
NumberAnimation {
duration: 100
}
}
states: [
State {
name: ""
// When not hovered, stay at default
when: !workspaceHoverHandler.hovered
PropertyChanges {
workspaceRowContainer.anchors.topMargin: -33
}
},
State {
name: "hovered"
// Keep the workspace dropped whenever hovered is true
when: workspaceHoverHandler.hovered
PropertyChanges {
workspaceRowContainer.anchors.topMargin: -20
}
PropertyChanges {
workspaceRowContainer.textBottomMargin: 7
}
}
]
Behavior on textBottomMargin {
NumberAnimation {
duration: 80
}
}
anchors {
left: parent.left
leftMargin: 15
top: parent.top
topMargin: -33
}
HoverHandler {
id: workspaceHoverHandler
}
Row {
id: workspaceRow
spacing: 5
Repeater {
model: Hyprland.workspaces.values
Item {
id: selectorContainer
property HyprlandWorkspace activeWorkspace: workspaceRowContainer.monitor.activeWorkspace
property HyprlandWorkspace currentWorkspace: Hyprland.workspaces.values[index]
property HyprlandWorkspace globalFocusedWorkspace: Hyprland.focusedWorkspace
required property int index
function getImplicitWidth() {
let implicitWidth = 25;
if (workspaceRowContainer.state == "hovered") {
implicitWidth += 5;
if (currentWorkspace.focused) {
implicitWidth += 20;
}
}
return implicitWidth;
}
function shouldBeVisible() {
let isSpecialWorkspace = !selectorContainer.currentWorkspace.name.includes("special:");
let isCurrentMonitor = Hyprland.workspaces.values[index].monitor
== workspaceRowContainer.monitor;
return isSpecialWorkspace && isCurrentMonitor;
}
implicitHeight: 50
implicitWidth: getImplicitWidth()
visible: shouldBeVisible()
Behavior on anchors.topMargin {
NumberAnimation {
duration: 100
}
}
Behavior on implicitWidth {
NumberAnimation {
duration: 100
}
}
states: [
State {
name: ""
when: !selectorHoverHandler.hovered
PropertyChanges {
selectorContainer.anchors.topMargin: 0
}
},
State {
name: "individualHovered"
when: selectorHoverHandler.hovered
PropertyChanges {
selectorContainer.anchors.topMargin: 10
}
},
State {
name: "workspaceSwitched"
PropertyChanges {
selectorContainer.anchors.topMargin: 5
}
}
]
// onActiveWorkspaceChanged: {
// if (workspaceRowContainer.monitor.activeWorkspace.id == "1") {}
// if (workspaceRowContainer.monitor.activeWorkspace.id == "4") {}
// }
onGlobalFocusedWorkspaceChanged: {
if (globalFocusedWorkspace.name === currentWorkspace.name && globalFocusedWorkspace.monitor
=== workspaceRowContainer.monitor && workspaceRowContainer.monitor.activeWorkspace.name
=== currentWorkspace.name) {
selectorContainer.state = "workspaceSwitched";
switchFeedbackTimer.restart();
}
}
Timer {
id: switchFeedbackTimer
interval: 200
repeat: false
onTriggered: selectorContainer.state = ""
}
anchors {
top: parent.top
topMargin: 0
}
HoverHandler {
id: selectorHoverHandler
}
MouseArea {
id: selectorMouseArea
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
onClicked: selectorContainer.currentWorkspace.activate()
}
RectangularShadow {
anchors.fill: selector
blur: 20
color: Colors.crust
offset.x: 3
offset.y: 3
radius: selector.radius
spread: 7
z: -1
}
Rectangle {
id: selector
anchors.fill: parent
color: selectorContainer.currentWorkspace.focused ? Colors.mauve :
workspaceRowContainer.monitor.activeWorkspace
== selectorContainer.currentWorkspace ? Colors.lavender : Colors.overlay1
radius: 8
state: ""
z: 1
Behavior on color {
ColorAnimation {
duration: 100
}
}
Text {
id: workspaceIdentifier
font.family: "FiraMono Nerd Font"
font.pixelSize: workspaceRowContainer.state == "hovered" ? 15 : 13
text: selectorContainer.currentWorkspace.name
anchors {
bottom: parent.bottom
bottomMargin: workspaceRowContainer.textBottomMargin
horizontalCenter: parent.horizontalCenter
}
}
}
}
}
Item {
implicitHeight: 40
implicitWidth: 20
Behavior on anchors.topMargin {
NumberAnimation {
duration: 100
}
}
anchors {
top: parent.top
topMargin: workspaceRowContainer.state == "hovered" ? 0 : -10
}
MouseArea {
id: newWorkspaceButton
anchors.fill: parent
onClicked: Hyprland.dispatch(`hl.dsp.focus({workspace =
${workspaceRowContainer.getNextEmptyWorkspaceId()}})`)
}
Rectangle {
anchors.fill: parent
border.color: Colors.text
border.width: 2
color: Colors.surface2
radius: 10
Text {
color: Colors.text
font.family: "FiraMono Nerd Font"
font.pixelSize: 20
text: "+"
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
}
}
}
}
}