mirror of
https://github.com/belsabbagh/dotfiles.git
synced 2026-04-11 09:36:46 +00:00
quickshell and hyprland additions
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.utils
|
||||
import qs.config
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property var bar
|
||||
required property Brightness.Monitor monitor
|
||||
property color colour: Colours.palette.m3primary
|
||||
|
||||
readonly property int maxHeight: {
|
||||
const otherModules = bar.children.filter(c => c.id && c.item !== this && c.id !== "spacer");
|
||||
const otherHeight = otherModules.reduce((acc, curr) => acc + (curr.item.nonAnimHeight ?? curr.height), 0);
|
||||
// Length - 2 cause repeater counts as a child
|
||||
return bar.height - otherHeight - bar.spacing * (bar.children.length - 1) - bar.vPadding * 2;
|
||||
}
|
||||
property Title current: text1
|
||||
|
||||
clip: true
|
||||
implicitWidth: Math.max(icon.implicitWidth, current.implicitHeight)
|
||||
implicitHeight: icon.implicitHeight + current.implicitWidth + current.anchors.topMargin
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
animate: true
|
||||
text: Icons.getAppCategoryIcon(Hypr.activeToplevel?.lastIpcObject.class, "desktop_windows")
|
||||
color: root.colour
|
||||
}
|
||||
|
||||
Title {
|
||||
id: text1
|
||||
}
|
||||
|
||||
Title {
|
||||
id: text2
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
|
||||
text: Hypr.activeToplevel?.title ?? qsTr("Desktop")
|
||||
font.pointSize: Appearance.font.size.smaller
|
||||
font.family: Appearance.font.family.mono
|
||||
elide: Qt.ElideRight
|
||||
elideWidth: root.maxHeight - icon.height
|
||||
|
||||
onTextChanged: {
|
||||
const next = root.current === text1 ? text2 : text1;
|
||||
next.text = elidedText;
|
||||
root.current = next;
|
||||
}
|
||||
onElideWidthChanged: root.current.text = elidedText
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
component Title: StyledText {
|
||||
id: text
|
||||
|
||||
anchors.horizontalCenter: icon.horizontalCenter
|
||||
anchors.top: icon.bottom
|
||||
anchors.topMargin: Appearance.spacing.small
|
||||
|
||||
font.pointSize: metrics.font.pointSize
|
||||
font.family: metrics.font.family
|
||||
color: root.colour
|
||||
opacity: root.current === this ? 1 : 0
|
||||
|
||||
transform: [
|
||||
Translate {
|
||||
x: Config.bar.activeWindow.inverted ? -implicitWidth + text.implicitHeight : 0
|
||||
},
|
||||
Rotation {
|
||||
angle: Config.bar.activeWindow.inverted ? 270 : 90
|
||||
origin.x: text.implicitHeight / 2
|
||||
origin.y: text.implicitHeight / 2
|
||||
}
|
||||
]
|
||||
|
||||
width: implicitHeight
|
||||
height: implicitWidth
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
import QtQuick
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property color colour: Colours.palette.m3tertiary
|
||||
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
Loader {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
active: Config.bar.clock.showIcon
|
||||
visible: active
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
text: "calendar_month"
|
||||
color: root.colour
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: text
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
horizontalAlignment: StyledText.AlignHCenter
|
||||
text: Time.format(Config.services.useTwelveHourClock ? "hh\nmm\nA" : "hh\nmm")
|
||||
font.pointSize: Appearance.font.size.smaller
|
||||
font.family: Appearance.font.family.mono
|
||||
color: root.colour
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import qs.components.effects
|
||||
import qs.services
|
||||
import qs.config
|
||||
import qs.utils
|
||||
import QtQuick
|
||||
import qs.components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
implicitWidth: Appearance.font.size.large * 1.2
|
||||
implicitHeight: Appearance.font.size.large * 1.2
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const visibilities = Visibilities.getForActive();
|
||||
visibilities.launcher = !visibilities.launcher;
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: SysInfo.isDefaultLogo ? caelestiaLogo : distroIcon
|
||||
}
|
||||
|
||||
Component {
|
||||
id: caelestiaLogo
|
||||
|
||||
Logo {
|
||||
implicitWidth: Appearance.font.size.large * 1.8
|
||||
implicitHeight: Appearance.font.size.large * 1.8
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: distroIcon
|
||||
|
||||
ColouredIcon {
|
||||
source: SysInfo.osLogo
|
||||
implicitSize: Appearance.font.size.large * 1.2
|
||||
colour: Colours.palette.m3tertiary
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
implicitWidth: icon.implicitHeight + Appearance.padding.small * 2
|
||||
implicitHeight: icon.implicitHeight
|
||||
|
||||
StateLayer {
|
||||
// Cursed workaround to make the height larger than the parent
|
||||
anchors.fill: undefined
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: icon.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
function onClicked(): void {
|
||||
root.visibilities.session = !root.visibilities.session;
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: -1
|
||||
|
||||
text: "power_settings_new"
|
||||
color: Colours.palette.m3error
|
||||
font.bold: true
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import qs.components
|
||||
import qs.modules.controlcenter
|
||||
import qs.services
|
||||
import qs.config
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
implicitWidth: icon.implicitHeight + Appearance.padding.small * 2
|
||||
implicitHeight: icon.implicitHeight
|
||||
|
||||
StateLayer {
|
||||
// Cursed workaround to make the height larger than the parent
|
||||
anchors.fill: undefined
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: icon.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
function onClicked(): void {
|
||||
WindowFactory.create(null, {
|
||||
active: "network"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: -1
|
||||
|
||||
text: "settings"
|
||||
color: Colours.palette.m3onSurface
|
||||
font.bold: true
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import qs.components
|
||||
import qs.modules.controlcenter
|
||||
import qs.services
|
||||
import qs.config
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
implicitWidth: icon.implicitHeight + Appearance.padding.small * 2
|
||||
implicitHeight: icon.implicitHeight
|
||||
|
||||
StateLayer {
|
||||
// Cursed workaround to make the height larger than the parent
|
||||
anchors.fill: undefined
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: icon.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
function onClicked(): void {
|
||||
WindowFactory.create(null, {
|
||||
active: "network"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: -1
|
||||
|
||||
text: "settings"
|
||||
color: Colours.palette.m3onSurface
|
||||
font.bold: true
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.utils
|
||||
import qs.config
|
||||
import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Services.UPower
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
property color colour: Colours.palette.m3secondary
|
||||
readonly property alias items: iconColumn
|
||||
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
clip: true
|
||||
implicitWidth: Config.bar.sizes.innerWidth
|
||||
implicitHeight: iconColumn.implicitHeight + Appearance.padding.normal * 2 - (Config.bar.status.showLockStatus && !Hypr.capsLock && !Hypr.numLock ? iconColumn.spacing : 0)
|
||||
|
||||
ColumnLayout {
|
||||
id: iconColumn
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Appearance.padding.normal
|
||||
|
||||
spacing: Appearance.spacing.smaller / 2
|
||||
|
||||
// Lock keys status
|
||||
WrappedLoader {
|
||||
name: "lockstatus"
|
||||
active: Config.bar.status.showLockStatus
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
implicitWidth: capslockIcon.implicitWidth
|
||||
implicitHeight: Hypr.capsLock ? capslockIcon.implicitHeight : 0
|
||||
|
||||
MaterialIcon {
|
||||
id: capslockIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
scale: Hypr.capsLock ? 1 : 0.5
|
||||
opacity: Hypr.capsLock ? 1 : 0
|
||||
|
||||
text: "keyboard_capslock_badge"
|
||||
color: root.colour
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.topMargin: Hypr.capsLock && Hypr.numLock ? iconColumn.spacing : 0
|
||||
|
||||
implicitWidth: numlockIcon.implicitWidth
|
||||
implicitHeight: Hypr.numLock ? numlockIcon.implicitHeight : 0
|
||||
|
||||
MaterialIcon {
|
||||
id: numlockIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
scale: Hypr.numLock ? 1 : 0.5
|
||||
opacity: Hypr.numLock ? 1 : 0
|
||||
|
||||
text: "looks_one"
|
||||
color: root.colour
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Audio icon
|
||||
WrappedLoader {
|
||||
name: "audio"
|
||||
active: Config.bar.status.showAudio
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
animate: true
|
||||
text: Icons.getVolumeIcon(Audio.volume, Audio.muted)
|
||||
color: root.colour
|
||||
}
|
||||
}
|
||||
|
||||
// Microphone icon
|
||||
WrappedLoader {
|
||||
name: "audio"
|
||||
active: Config.bar.status.showMicrophone
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
animate: true
|
||||
text: Icons.getMicVolumeIcon(Audio.sourceVolume, Audio.sourceMuted)
|
||||
color: root.colour
|
||||
}
|
||||
}
|
||||
|
||||
// Keyboard layout icon
|
||||
WrappedLoader {
|
||||
name: "kblayout"
|
||||
active: Config.bar.status.showKbLayout
|
||||
|
||||
sourceComponent: StyledText {
|
||||
animate: true
|
||||
text: Hypr.kbLayout
|
||||
color: root.colour
|
||||
font.family: Appearance.font.family.mono
|
||||
}
|
||||
}
|
||||
|
||||
// Network icon
|
||||
WrappedLoader {
|
||||
name: "network"
|
||||
active: Config.bar.status.showNetwork && (!Nmcli.activeEthernet || Config.bar.status.showWifi)
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
animate: true
|
||||
text: Nmcli.active ? Icons.getNetworkIcon(Nmcli.active.strength ?? 0) : "wifi_off"
|
||||
color: root.colour
|
||||
}
|
||||
}
|
||||
|
||||
// Ethernet icon
|
||||
WrappedLoader {
|
||||
name: "ethernet"
|
||||
active: Config.bar.status.showNetwork && Nmcli.activeEthernet
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
animate: true
|
||||
text: "cable"
|
||||
color: root.colour
|
||||
}
|
||||
}
|
||||
|
||||
// Bluetooth section
|
||||
WrappedLoader {
|
||||
Layout.preferredHeight: implicitHeight
|
||||
|
||||
name: "bluetooth"
|
||||
active: Config.bar.status.showBluetooth
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.smaller / 2
|
||||
|
||||
// Bluetooth icon
|
||||
MaterialIcon {
|
||||
animate: true
|
||||
text: {
|
||||
if (!Bluetooth.defaultAdapter?.enabled)
|
||||
return "bluetooth_disabled";
|
||||
if (Bluetooth.devices.values.some(d => d.connected))
|
||||
return "bluetooth_connected";
|
||||
return "bluetooth";
|
||||
}
|
||||
color: root.colour
|
||||
}
|
||||
|
||||
// Connected bluetooth devices
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: Bluetooth.devices.values.filter(d => d.state !== BluetoothDeviceState.Disconnected)
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: device
|
||||
|
||||
required property BluetoothDevice modelData
|
||||
|
||||
animate: true
|
||||
text: Icons.getBluetoothIcon(modelData?.icon)
|
||||
color: root.colour
|
||||
fill: 1
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
running: device.modelData?.state !== BluetoothDeviceState.Connected
|
||||
alwaysRunToEnd: true
|
||||
loops: Animation.Infinite
|
||||
|
||||
Anim {
|
||||
from: 1
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||
}
|
||||
Anim {
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on Layout.preferredHeight {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
// Battery icon
|
||||
WrappedLoader {
|
||||
name: "battery"
|
||||
active: Config.bar.status.showBattery
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
animate: true
|
||||
text: {
|
||||
if (!UPower.displayDevice.isLaptopBattery) {
|
||||
if (PowerProfiles.profile === PowerProfile.PowerSaver)
|
||||
return "energy_savings_leaf";
|
||||
if (PowerProfiles.profile === PowerProfile.Performance)
|
||||
return "rocket_launch";
|
||||
return "balance";
|
||||
}
|
||||
|
||||
const perc = UPower.displayDevice.percentage;
|
||||
const charging = [UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged, UPowerDeviceState.PendingCharge].includes(UPower.displayDevice.state);
|
||||
if (perc === 1)
|
||||
return charging ? "battery_charging_full" : "battery_full";
|
||||
let level = Math.floor(perc * 7);
|
||||
if (charging && (level === 4 || level === 1))
|
||||
level--;
|
||||
return charging ? `battery_charging_${(level + 3) * 10}` : `battery_${level}_bar`;
|
||||
}
|
||||
color: !UPower.onBattery || UPower.displayDevice.percentage > 0.2 ? root.colour : Colours.palette.m3error
|
||||
fill: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component WrappedLoader: Loader {
|
||||
required property string name
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
visible: active
|
||||
}
|
||||
}
|
||||
121
.config/quickshell/caelestia/modules/bar/components/Tray.qml
Normal file
121
.config/quickshell/caelestia/modules/bar/components/Tray.qml
Normal file
@@ -0,0 +1,121 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
import Quickshell
|
||||
import Quickshell.Services.SystemTray
|
||||
import QtQuick
|
||||
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
readonly property alias layout: layout
|
||||
readonly property alias items: items
|
||||
readonly property alias expandIcon: expandIcon
|
||||
|
||||
readonly property int padding: Config.bar.tray.background ? Appearance.padding.normal : Appearance.padding.small
|
||||
readonly property int spacing: Config.bar.tray.background ? Appearance.spacing.small : 0
|
||||
|
||||
property bool expanded
|
||||
|
||||
readonly property real nonAnimHeight: {
|
||||
if (!Config.bar.tray.compact)
|
||||
return layout.implicitHeight + padding * 2;
|
||||
return (expanded ? expandIcon.implicitHeight + layout.implicitHeight + spacing : expandIcon.implicitHeight) + padding * 2;
|
||||
}
|
||||
|
||||
clip: true
|
||||
visible: height > 0
|
||||
|
||||
implicitWidth: Config.bar.sizes.innerWidth
|
||||
implicitHeight: nonAnimHeight
|
||||
|
||||
color: Qt.alpha(Colours.tPalette.m3surfaceContainer, (Config.bar.tray.background && items.count > 0) ? Colours.tPalette.m3surfaceContainer.a : 0)
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Column {
|
||||
id: layout
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: root.padding
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
opacity: root.expanded || !Config.bar.tray.compact ? 1 : 0
|
||||
|
||||
add: Transition {
|
||||
Anim {
|
||||
properties: "scale"
|
||||
from: 0
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
}
|
||||
|
||||
move: Transition {
|
||||
Anim {
|
||||
properties: "scale"
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
Anim {
|
||||
properties: "x,y"
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: items
|
||||
|
||||
model: ScriptModel {
|
||||
values: SystemTray.items.values.filter(i => !Config.bar.tray.hiddenIcons.includes(i.id))
|
||||
}
|
||||
|
||||
TrayItem {}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: expandIcon
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
active: Config.bar.tray.compact && items.count > 0
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitWidth: expandIconInner.implicitWidth
|
||||
implicitHeight: expandIconInner.implicitHeight - Appearance.padding.small * 2
|
||||
|
||||
MaterialIcon {
|
||||
id: expandIconInner
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Config.bar.tray.background ? Appearance.padding.small : -Appearance.padding.small
|
||||
text: "expand_less"
|
||||
font.pointSize: Appearance.font.size.large
|
||||
rotation: root.expanded ? 180 : 0
|
||||
|
||||
Behavior on rotation {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on anchors.bottomMargin {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components.effects
|
||||
import qs.services
|
||||
import qs.config
|
||||
import qs.utils
|
||||
import Quickshell.Services.SystemTray
|
||||
import QtQuick
|
||||
|
||||
MouseArea {
|
||||
id: root
|
||||
|
||||
required property SystemTrayItem modelData
|
||||
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
implicitWidth: Appearance.font.size.small * 2
|
||||
implicitHeight: Appearance.font.size.small * 2
|
||||
|
||||
onClicked: event => {
|
||||
if (event.button === Qt.LeftButton)
|
||||
modelData.activate();
|
||||
else
|
||||
modelData.secondaryActivate();
|
||||
}
|
||||
|
||||
ColouredIcon {
|
||||
id: icon
|
||||
|
||||
anchors.fill: parent
|
||||
source: Icons.getTrayIcon(root.modelData.id, root.modelData.icon)
|
||||
colour: Colours.palette.m3secondary
|
||||
layer.enabled: Config.bar.tray.recolour
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import qs.components
|
||||
import qs.components.effects
|
||||
import qs.services
|
||||
import qs.config
|
||||
import QtQuick
|
||||
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
required property int activeWsId
|
||||
required property Repeater workspaces
|
||||
required property Item mask
|
||||
|
||||
readonly property int currentWsIdx: {
|
||||
let i = activeWsId - 1;
|
||||
while (i < 0)
|
||||
i += Config.bar.workspaces.shown;
|
||||
return i % Config.bar.workspaces.shown;
|
||||
}
|
||||
|
||||
property real leading: workspaces.count > 0 ? workspaces.itemAt(currentWsIdx)?.y ?? 0 : 0
|
||||
property real trailing: workspaces.count > 0 ? workspaces.itemAt(currentWsIdx)?.y ?? 0 : 0
|
||||
property real currentSize: workspaces.count > 0 ? workspaces.itemAt(currentWsIdx)?.size ?? 0 : 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) {
|
||||
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
|
||||
|
||||
onCurrentWsIdxChanged: {
|
||||
lastWs = cWs;
|
||||
cWs = currentWsIdx;
|
||||
}
|
||||
|
||||
clip: true
|
||||
y: offset + mask.y
|
||||
implicitWidth: Config.bar.sizes.innerWidth - Appearance.padding.small * 2
|
||||
implicitHeight: size
|
||||
radius: Appearance.rounding.full
|
||||
color: Colours.palette.m3primary
|
||||
|
||||
Colouriser {
|
||||
source: root.mask
|
||||
sourceColor: Colours.palette.m3onSurface
|
||||
colorizationColor: Colours.palette.m3onPrimary
|
||||
|
||||
x: 0
|
||||
y: -parent.offset
|
||||
implicitWidth: root.mask.implicitWidth
|
||||
implicitHeight: root.mask.implicitHeight
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
Behavior on leading {
|
||||
enabled: Config.bar.workspaces.activeTrail
|
||||
|
||||
EAnim {}
|
||||
}
|
||||
|
||||
Behavior on trailing {
|
||||
enabled: Config.bar.workspaces.activeTrail
|
||||
|
||||
EAnim {
|
||||
duration: Appearance.anim.durations.normal * 2
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on currentSize {
|
||||
enabled: Config.bar.workspaces.activeTrail
|
||||
|
||||
EAnim {}
|
||||
}
|
||||
|
||||
Behavior on offset {
|
||||
enabled: !Config.bar.workspaces.activeTrail
|
||||
|
||||
EAnim {}
|
||||
}
|
||||
|
||||
Behavior on size {
|
||||
enabled: !Config.bar.workspaces.activeTrail
|
||||
|
||||
EAnim {}
|
||||
}
|
||||
|
||||
component EAnim: Anim {
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Repeater workspaces
|
||||
required property var occupied
|
||||
required property int groupOffset
|
||||
|
||||
property list<var> pills: []
|
||||
|
||||
onOccupiedChanged: {
|
||||
if (!occupied)
|
||||
return;
|
||||
let count = 0;
|
||||
const start = groupOffset;
|
||||
const end = start + Config.bar.workspaces.shown;
|
||||
for (const [ws, occ] of Object.entries(occupied)) {
|
||||
if (ws > start && ws <= end && occ) {
|
||||
const isFirstInGroup = Number(ws) === start + 1;
|
||||
const isLastInGroup = Number(ws) === end;
|
||||
if (isFirstInGroup || !occupied[ws - 1]) {
|
||||
if (pills[count])
|
||||
pills[count].start = ws;
|
||||
else
|
||||
pills.push(pillComp.createObject(root, {
|
||||
start: ws
|
||||
}));
|
||||
count++;
|
||||
}
|
||||
if ((isLastInGroup || !occupied[ws + 1]) && pills[count - 1])
|
||||
pills[count - 1].end = ws;
|
||||
}
|
||||
}
|
||||
if (pills.length > count)
|
||||
pills.splice(count, pills.length - count).forEach(p => p.destroy());
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.pills.filter(p => p)
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
id: rect
|
||||
|
||||
required property var modelData
|
||||
|
||||
readonly property Workspace start: root.workspaces.count > 0 ? root.workspaces.itemAt(getWsIdx(modelData.start)) ?? null : null
|
||||
readonly property Workspace end: root.workspaces.count > 0 ? root.workspaces.itemAt(getWsIdx(modelData.end)) ?? null : null
|
||||
|
||||
function getWsIdx(ws: int): int {
|
||||
let i = ws - 1;
|
||||
while (i < 0)
|
||||
i += Config.bar.workspaces.shown;
|
||||
return i % Config.bar.workspaces.shown;
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: root.horizontalCenter
|
||||
|
||||
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
|
||||
|
||||
color: Colours.layer(Colours.palette.m3surfaceContainerHigh, 2)
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
scale: 0
|
||||
Component.onCompleted: scale = 1
|
||||
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Pill: QtObject {
|
||||
property int start
|
||||
property int end
|
||||
}
|
||||
|
||||
Component {
|
||||
id: pillComp
|
||||
|
||||
Pill {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.components.effects
|
||||
import qs.services
|
||||
import qs.utils
|
||||
import qs.config
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
readonly property HyprlandMonitor monitor: Hypr.monitorFor(screen)
|
||||
readonly property string activeSpecial: (Config.bar.workspaces.perMonitorWorkspaces ? monitor : Hypr.focusedMonitor)?.lastIpcObject?.specialWorkspace?.name ?? ""
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: mask
|
||||
}
|
||||
|
||||
Item {
|
||||
id: mask
|
||||
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
visible: false
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Vertical
|
||||
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: Qt.rgba(0, 0, 0, 0)
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.3
|
||||
color: Qt.rgba(0, 0, 0, 1)
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.7
|
||||
color: Qt.rgba(0, 0, 0, 1)
|
||||
}
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: Qt.rgba(0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
implicitHeight: parent.height / 2
|
||||
opacity: view.contentY > 0 ? 0 : 1
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
implicitHeight: parent.height / 2
|
||||
opacity: view.contentY < view.contentHeight - parent.height + Appearance.padding.small ? 0 : 1
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: view
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: Appearance.spacing.normal
|
||||
interactive: false
|
||||
|
||||
currentIndex: model.values.findIndex(w => w.name === root.activeSpecial)
|
||||
onCurrentIndexChanged: currentIndex = Qt.binding(() => model.values.findIndex(w => w.name === root.activeSpecial))
|
||||
|
||||
model: ScriptModel {
|
||||
values: Hypr.workspaces.values.filter(w => w.name.startsWith("special:") && (!Config.bar.workspaces.perMonitorWorkspaces || w.monitor === root.monitor))
|
||||
}
|
||||
|
||||
preferredHighlightBegin: 0
|
||||
preferredHighlightEnd: height
|
||||
highlightRangeMode: ListView.StrictlyEnforceRange
|
||||
|
||||
highlightFollowsCurrentItem: false
|
||||
highlight: Item {
|
||||
y: view.currentItem?.y ?? 0
|
||||
implicitHeight: view.currentItem?.size ?? 0
|
||||
|
||||
Behavior on y {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
id: ws
|
||||
|
||||
required property HyprlandWorkspace modelData
|
||||
readonly property int size: label.Layout.preferredHeight + (hasWindows ? windows.implicitHeight + Appearance.padding.small : 0)
|
||||
property int wsId
|
||||
property string icon
|
||||
property bool hasWindows
|
||||
|
||||
anchors.left: view.contentItem.left
|
||||
anchors.right: view.contentItem.right
|
||||
|
||||
spacing: 0
|
||||
|
||||
Component.onCompleted: {
|
||||
wsId = modelData.id;
|
||||
icon = Icons.getSpecialWsIcon(modelData.name);
|
||||
hasWindows = Config.bar.workspaces.showWindowsOnSpecialWorkspaces && modelData.lastIpcObject.windows > 0;
|
||||
}
|
||||
|
||||
// Hacky thing cause modelData gets destroyed before the remove anim finishes
|
||||
Connections {
|
||||
target: ws.modelData
|
||||
|
||||
function onIdChanged(): void {
|
||||
if (ws.modelData)
|
||||
ws.wsId = ws.modelData.id;
|
||||
}
|
||||
|
||||
function onNameChanged(): void {
|
||||
if (ws.modelData)
|
||||
ws.icon = Icons.getSpecialWsIcon(ws.modelData.name);
|
||||
}
|
||||
|
||||
function onLastIpcObjectChanged(): void {
|
||||
if (ws.modelData)
|
||||
ws.hasWindows = Config.bar.workspaces.showWindowsOnSpecialWorkspaces && ws.modelData.lastIpcObject.windows > 0;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Config.bar.workspaces
|
||||
|
||||
function onShowWindowsOnSpecialWorkspacesChanged(): void {
|
||||
if (ws.modelData)
|
||||
ws.hasWindows = Config.bar.workspaces.showWindowsOnSpecialWorkspaces && ws.modelData.lastIpcObject.windows > 0;
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: label
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
|
||||
Layout.preferredHeight: Config.bar.sizes.innerWidth - Appearance.padding.small * 2
|
||||
|
||||
sourceComponent: ws.icon.length === 1 ? letterComp : iconComp
|
||||
|
||||
Component {
|
||||
id: iconComp
|
||||
|
||||
MaterialIcon {
|
||||
fill: 1
|
||||
text: ws.icon
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: letterComp
|
||||
|
||||
StyledText {
|
||||
text: ws.icon
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: windows
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredHeight: implicitHeight
|
||||
|
||||
visible: active
|
||||
active: ws.hasWindows
|
||||
|
||||
sourceComponent: Column {
|
||||
spacing: 0
|
||||
|
||||
add: Transition {
|
||||
Anim {
|
||||
properties: "scale"
|
||||
from: 0
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
}
|
||||
|
||||
move: Transition {
|
||||
Anim {
|
||||
properties: "scale"
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
Anim {
|
||||
properties: "x,y"
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: Hypr.toplevels.values.filter(c => c.workspace?.id === ws.wsId)
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
required property var modelData
|
||||
|
||||
grade: 0
|
||||
text: Icons.getAppCategoryIcon(modelData.lastIpcObject.class, "terminal")
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on Layout.preferredHeight {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add: Transition {
|
||||
Anim {
|
||||
properties: "scale"
|
||||
from: 0
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
}
|
||||
|
||||
remove: Transition {
|
||||
Anim {
|
||||
property: "scale"
|
||||
to: 0.5
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
}
|
||||
|
||||
move: Transition {
|
||||
Anim {
|
||||
properties: "scale"
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
Anim {
|
||||
properties: "x,y"
|
||||
}
|
||||
}
|
||||
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
properties: "scale"
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
Anim {
|
||||
properties: "x,y"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: Config.bar.workspaces.activeIndicator
|
||||
anchors.fill: parent
|
||||
|
||||
sourceComponent: Item {
|
||||
StyledClippingRect {
|
||||
id: indicator
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
y: (view.currentItem?.y ?? 0) - view.contentY
|
||||
implicitHeight: view.currentItem?.size ?? 0
|
||||
|
||||
color: Colours.palette.m3tertiary
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Colouriser {
|
||||
source: view
|
||||
sourceColor: Colours.palette.m3onSurface
|
||||
colorizationColor: Colours.palette.m3onTertiary
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
x: 0
|
||||
y: -indicator.y
|
||||
implicitWidth: view.width
|
||||
implicitHeight: view.height
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
Anim {
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
property real startY
|
||||
|
||||
anchors.fill: view
|
||||
|
||||
drag.target: view.contentItem
|
||||
drag.axis: Drag.YAxis
|
||||
drag.maximumY: 0
|
||||
drag.minimumY: Math.min(0, view.height - view.contentHeight - Appearance.padding.small)
|
||||
|
||||
onPressed: event => startY = event.y
|
||||
|
||||
onClicked: event => {
|
||||
if (Math.abs(event.y - startY) > drag.threshold)
|
||||
return;
|
||||
|
||||
const ws = view.itemAt(event.x, event.y);
|
||||
if (ws?.modelData)
|
||||
Hypr.dispatch(`togglespecialworkspace ${ws.modelData.name.slice(8)}`);
|
||||
else
|
||||
Hypr.dispatch("togglespecialworkspace special");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.utils
|
||||
import qs.config
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property int index
|
||||
required property int activeWsId
|
||||
required property var occupied
|
||||
required property int groupOffset
|
||||
|
||||
readonly property bool isWorkspace: true // Flag for finding workspace children
|
||||
// Unanimated prop for others to use as reference
|
||||
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.alignment: Qt.AlignHCenter
|
||||
Layout.preferredHeight: size
|
||||
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
id: indicator
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
|
||||
Layout.preferredHeight: Config.bar.sizes.innerWidth - Appearance.padding.small * 2
|
||||
|
||||
animate: true
|
||||
text: {
|
||||
const ws = Hypr.workspaces.values.find(w => w.id === root.ws);
|
||||
const wsName = !ws || ws.name == root.ws ? root.ws : ws.name[0];
|
||||
let displayName = wsName.toString();
|
||||
if (Config.bar.workspaces.capitalisation.toLowerCase() === "upper") {
|
||||
displayName = displayName.toUpperCase();
|
||||
} else if (Config.bar.workspaces.capitalisation.toLowerCase() === "lower") {
|
||||
displayName = displayName.toLowerCase();
|
||||
}
|
||||
const label = Config.bar.workspaces.label || displayName;
|
||||
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)
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: windows
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillHeight: true
|
||||
Layout.topMargin: -Config.bar.sizes.innerWidth / 10
|
||||
|
||||
visible: active
|
||||
active: root.hasWindows
|
||||
|
||||
sourceComponent: Column {
|
||||
spacing: 0
|
||||
|
||||
add: Transition {
|
||||
Anim {
|
||||
properties: "scale"
|
||||
from: 0
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
}
|
||||
|
||||
move: Transition {
|
||||
Anim {
|
||||
properties: "scale"
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
Anim {
|
||||
properties: "x,y"
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: Hypr.toplevels.values.filter(c => c.workspace?.id === root.ws)
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
required property var modelData
|
||||
|
||||
grade: 0
|
||||
text: Icons.getAppCategoryIcon(modelData.lastIpcObject.class, "terminal")
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on Layout.preferredHeight {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.services
|
||||
import qs.config
|
||||
import qs.components
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
|
||||
StyledClippingRect {
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
|
||||
readonly property bool onSpecial: (Config.bar.workspaces.perMonitorWorkspaces ? Hypr.monitorFor(screen) : Hypr.focusedMonitor)?.lastIpcObject?.specialWorkspace?.name !== ""
|
||||
readonly property int activeWsId: Config.bar.workspaces.perMonitorWorkspaces ? (Hypr.monitorFor(screen).activeWorkspace?.id ?? 1) : Hypr.activeWsId
|
||||
|
||||
readonly property var occupied: Hypr.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
|
||||
|
||||
property real blur: onSpecial ? 1 : 0
|
||||
|
||||
implicitWidth: Config.bar.sizes.innerWidth
|
||||
implicitHeight: layout.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
scale: root.onSpecial ? 0.8 : 1
|
||||
opacity: root.onSpecial ? 0.5 : 1
|
||||
|
||||
layer.enabled: root.blur > 0
|
||||
layer.effect: MultiEffect {
|
||||
blurEnabled: true
|
||||
blur: root.blur
|
||||
blurMax: 32
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: Config.bar.workspaces.occupiedBg
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.small
|
||||
|
||||
sourceComponent: OccupiedBg {
|
||||
workspaces: workspaces
|
||||
occupied: root.occupied
|
||||
groupOffset: root.groupOffset
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Math.floor(Appearance.spacing.small / 2)
|
||||
|
||||
Repeater {
|
||||
id: workspaces
|
||||
|
||||
model: Config.bar.workspaces.shown
|
||||
|
||||
Workspace {
|
||||
activeWsId: root.activeWsId
|
||||
occupied: root.occupied
|
||||
groupOffset: root.groupOffset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
active: Config.bar.workspaces.activeIndicator
|
||||
|
||||
sourceComponent: ActiveIndicator {
|
||||
activeWsId: root.activeWsId
|
||||
workspaces: workspaces
|
||||
mask: layout
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: layout
|
||||
onClicked: event => {
|
||||
const ws = layout.childAt(event.x, event.y).ws;
|
||||
if (Hypr.activeWsId !== ws)
|
||||
Hypr.dispatch(`workspace ${ws}`);
|
||||
else
|
||||
Hypr.dispatch("togglespecialworkspace special");
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: specialWs
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.small
|
||||
|
||||
active: opacity > 0
|
||||
|
||||
scale: root.onSpecial ? 1 : 0.5
|
||||
opacity: root.onSpecial ? 1 : 0
|
||||
|
||||
sourceComponent: SpecialWorkspaces {
|
||||
screen: root.screen
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on blur {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user