bar/workspaces: refactor

This commit is contained in:
2 * r + 2 * t 2025-08-16 00:10:48 +10:00
parent 2be46ff65a
commit d8f7bac960
9 changed files with 98 additions and 111 deletions

View file

@ -254,13 +254,12 @@ All configuration options are in `~/.config/caelestia/shell.json`.
},
"workspaces": {
"activeIndicator": true,
"activeLabel": "󰮯 ",
"activeLabel": "󰮯",
"activeTrail": false,
"label": " ",
"occupiedBg": false,
"occupiedLabel": "󰮯 ",
"occupiedLabel": "󰮯",
"perMonitorWorkspaces": true,
"rounded": true,
"showWindows": true,
"shown": 5
}

View file

@ -50,15 +50,14 @@ JsonObject {
component Workspaces: JsonObject {
property int shown: 5
property bool rounded: true
property bool activeIndicator: true
property bool occupiedBg: false
property bool showWindows: true
property bool activeTrail: false
property bool perMonitorWorkspaces: true
property string label: " "
property string occupiedLabel: "󰮯 "
property string activeLabel: "󰮯 "
property string occupiedLabel: "󰮯"
property string activeLabel: "󰮯"
}
component Tray: JsonObject {

View file

@ -177,6 +177,5 @@ ColumnLayout {
visible: enabled
active: enabled
asynchronous: true
}
}

View file

@ -73,7 +73,6 @@ Item {
anchors.right: parent.right
active: root.shouldBeVisible || root.visible
asynchronous: true
sourceComponent: Bar {
width: root.contentWidth

View file

@ -8,45 +8,38 @@ StyledRect {
id: root
required property int activeWsId
required property list<Workspace> workspaces
required property Repeater workspaces
required property Item mask
required property real maskWidth
required property real maskHeight
required property int groupOffset
readonly property int currentWsIdx: activeWsId - 1 - groupOffset
property real leading: getWsY(currentWsIdx)
property real trailing: getWsY(currentWsIdx)
property real currentSize: workspaces[currentWsIdx]?.size ?? 0
readonly property int currentWsIdx: (activeWsId - 1) % Config.bar.workspaces.shown
property real leading: workspaces.itemAt(currentWsIdx)?.y ?? 0
property real trailing: workspaces.itemAt(currentWsIdx)?.y ?? 0
property real currentSize: workspaces.itemAt(currentWsIdx)?.size ?? 0
property real offset: Math.min(leading, trailing)
property real size: {
const s = Math.abs(leading - trailing) + currentSize;
if (Config.bar.workspaces.activeTrail && lastWs > currentWsIdx)
return Math.min(getWsY(lastWs) + (workspaces[lastWs]?.size ?? 0) - offset, s);
if (Config.bar.workspaces.activeTrail && lastWs > currentWsIdx) {
const ws = workspaces.itemAt(lastWs);
// console.log(ws, lastWs);
return ws ? Math.min(ws.y + ws.size - offset, s) : 0;
}
return s;
}
property int cWs
property int lastWs
function getWsY(idx: int): real {
let y = 0;
for (let i = 0; i < idx; i++)
y += workspaces[i]?.size ?? 0;
return y;
}
onCurrentWsIdxChanged: {
lastWs = cWs;
cWs = currentWsIdx;
}
clip: true
x: 1
y: offset + 1
implicitWidth: Config.bar.sizes.innerHeight - 2
implicitHeight: size - 2
radius: Config.bar.workspaces.rounded ? Appearance.rounding.full : 0
y: offset + mask.y
implicitWidth: Config.bar.sizes.innerWidth - Appearance.padding.small * 2
implicitHeight: size
radius: Appearance.rounding.full
color: Colours.palette.m3primary
Colouriser {
@ -56,8 +49,8 @@ StyledRect {
x: 0
y: -parent.offset
implicitWidth: root.maskWidth
implicitHeight: root.maskHeight
implicitWidth: root.mask.implicitWidth
implicitHeight: root.mask.implicitHeight
anchors.horizontalCenter: parent.horizontalCenter
}

View file

@ -9,7 +9,7 @@ import QtQuick
Item {
id: root
required property list<Workspace> workspaces
required property Repeater workspaces
required property var occupied
required property int groupOffset
@ -48,18 +48,17 @@ Item {
required property var modelData
readonly property Workspace start: root.workspaces[modelData.start - 1 - root.groupOffset] ?? null
readonly property Workspace end: root.workspaces[modelData.end - 1 - root.groupOffset] ?? null
readonly property Workspace start: root.workspaces.itemAt((modelData.start - 1) % Config.bar.workspaces.shown) ?? null
readonly property Workspace end: root.workspaces.itemAt((modelData.end - 1) % Config.bar.workspaces.shown) ?? null
color: Colours.tPalette.m3surfaceContainerHigh
radius: Config.bar.workspaces.rounded ? Appearance.rounding.full : 0
anchors.horizontalCenter: root.horizontalCenter
x: start?.x ?? 0
y: start?.y ?? 0
implicitWidth: Config.bar.sizes.innerHeight
implicitHeight: end?.y + end?.height - start?.y
y: (start?.y ?? 0) - 1
implicitWidth: Config.bar.sizes.innerWidth - Appearance.padding.small * 2 + 2
implicitHeight: start && end ? end.y + end.size - start.y + 2 : 0
anchors.horizontalCenter: parent.horizontalCenter
color: Colours.layer(Colours.palette.m3surfaceContainerHigh, 2)
radius: Appearance.rounding.full
scale: 0
Component.onCompleted: scale = 1
@ -70,11 +69,11 @@ Item {
}
}
Behavior on x {
Behavior on y {
Anim {}
}
Behavior on y {
Behavior on implicitHeight {
Anim {}
}
}

View file

@ -6,7 +6,7 @@ import Quickshell
import QtQuick
import QtQuick.Layouts
Item {
ColumnLayout {
id: root
required property int index
@ -16,41 +16,44 @@ Item {
readonly property bool isWorkspace: true // Flag for finding workspace children
// Unanimated prop for others to use as reference
readonly property real size: childrenRect.height + (hasWindows ? Appearance.padding.smaller : 0)
readonly property int size: implicitHeight + (hasWindows ? Appearance.padding.small : 0)
readonly property int ws: groupOffset + index + 1
readonly property bool isOccupied: occupied[ws] ?? false
readonly property bool hasWindows: isOccupied && Config.bar.workspaces.showWindows
Layout.preferredWidth: childrenRect.width
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: size
spacing: 0
StyledText {
id: indicator
readonly property string label: Config.bar.workspaces.label || root.ws
readonly property string occupiedLabel: Config.bar.workspaces.occupiedLabel || label
readonly property string activeLabel: Config.bar.workspaces.activeLabel || (root.isOccupied ? occupiedLabel : label)
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Layout.preferredHeight: Config.bar.sizes.innerWidth - Appearance.padding.small * 2
animate: true
text: root.activeWsId === root.ws ? activeLabel : root.isOccupied ? occupiedLabel : label
text: {
const label = Config.bar.workspaces.label || root.ws;
const occupiedLabel = Config.bar.workspaces.occupiedLabel || label;
const activeLabel = Config.bar.workspaces.activeLabel || (root.isOccupied ? occupiedLabel : label);
return root.activeWsId === root.ws ? activeLabel : root.isOccupied ? occupiedLabel : label;
}
color: Config.bar.workspaces.occupiedBg || root.isOccupied || root.activeWsId === root.ws ? Colours.palette.m3onSurface : Colours.layer(Colours.palette.m3outlineVariant, 2)
horizontalAlignment: StyledText.AlignHCenter
verticalAlignment: StyledText.AlignVCenter
width: Config.bar.sizes.innerHeight
height: Config.bar.sizes.innerHeight
verticalAlignment: Qt.AlignVCenter
}
Loader {
id: windows
active: Config.bar.workspaces.showWindows
asynchronous: true
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
Layout.topMargin: -Config.bar.sizes.innerWidth / 10
anchors.horizontalCenter: indicator.horizontalCenter
anchors.top: indicator.bottom
anchors.topMargin: -Config.bar.sizes.innerHeight / 10
visible: active
active: root.hasWindows
asynchronous: true
sourceComponent: Column {
spacing: 0
@ -91,10 +94,6 @@ Item {
}
}
Behavior on Layout.preferredWidth {
Anim {}
}
Behavior on Layout.preferredHeight {
Anim {}
}

View file

@ -13,76 +13,61 @@ StyledRect {
required property ShellScreen screen
readonly property int activeWsId: Config.bar.workspaces.perMonitorWorkspaces ? (Hyprland.monitorFor(screen).activeWorkspace?.id ?? 1) : Hyprland.activeWsId
readonly property list<Workspace> workspaces: layout.children.filter(c => c.isWorkspace).sort((w1, w2) => w1.ws - w2.ws)
readonly property var occupied: Hyprland.workspaces.values.reduce((acc, curr) => {
acc[curr.id] = curr.lastIpcObject.windows > 0;
return acc;
}, {})
readonly property int groupOffset: Math.floor((activeWsId - 1) / Config.bar.workspaces.shown) * Config.bar.workspaces.shown
implicitWidth: layout.implicitWidth + Appearance.padding.small * 2
implicitHeight: layout.implicitHeight + Appearance.padding.small * 2
implicitWidth: Config.bar.sizes.innerWidth
color: Colours.tPalette.m3surfaceContainer
radius: Appearance.rounding.full
Item {
id: inner
Loader {
active: Config.bar.workspaces.occupiedBg
asynchronous: true
anchors.fill: parent
anchors.margins: Appearance.padding.small
ColumnLayout {
id: layout
spacing: 0
Repeater {
model: Config.bar.workspaces.shown
Workspace {
activeWsId: root.activeWsId
occupied: root.occupied
groupOffset: root.groupOffset
}
}
sourceComponent: OccupiedBg {
workspaces: workspaces
occupied: root.occupied
groupOffset: root.groupOffset
}
}
Loader {
active: Config.bar.workspaces.occupiedBg
asynchronous: true
ColumnLayout {
id: layout
z: -1
anchors.fill: parent
anchors.centerIn: parent
spacing: Math.floor(Appearance.spacing.small / 2)
sourceComponent: OccupiedBg {
workspaces: root.workspaces
Repeater {
id: workspaces
model: Config.bar.workspaces.shown
Workspace {
activeWsId: root.activeWsId
occupied: root.occupied
groupOffset: root.groupOffset
}
}
}
Loader {
active: Config.bar.workspaces.activeIndicator
asynchronous: true
Loader {
anchors.horizontalCenter: parent.horizontalCenter
active: Config.bar.workspaces.activeIndicator
asynchronous: true
sourceComponent: ActiveIndicator {
activeWsId: root.activeWsId
workspaces: root.workspaces
mask: layout
maskWidth: inner.width
maskHeight: inner.height
groupOffset: root.groupOffset
}
}
MouseArea {
anchors.fill: parent
onPressed: event => {
const ws = layout.childAt(event.x, event.y).index + root.groupOffset + 1;
if (Hyprland.activeWsId !== ws)
Hyprland.dispatch(`workspace ${ws}`);
}
sourceComponent: ActiveIndicator {
activeWsId: root.activeWsId
workspaces: workspaces
mask: layout
}
}
}

View file

@ -183,4 +183,19 @@ Singleton {
return "volume_down";
return "volume_mute";
}
function getSpecialWsIcon(name: string): string {
name = name.toLowerCase().slice("special:".length);
if (name === "special")
return "star";
if (name === "communication")
return "forum";
if (name === "music")
return "music_note";
if (name === "todo")
return "checklist";
if (name === "sysmon")
return "monitor_heart";
return name[0].toUpperCase();
}
}