Rearrange for tidier structure (#2212)

This commit is contained in:
clsty
2025-10-16 07:19:55 +08:00
parent 13065d7e5a
commit 8b493e091d
529 changed files with 165 additions and 138 deletions
@@ -0,0 +1,64 @@
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import QtQuick.Layouts
import "./../bar" as Bar
MouseArea {
id: root
property bool borderless: Config.options.bar.borderless
readonly property var chargeState: Battery.chargeState
readonly property bool isCharging: Battery.isCharging
readonly property bool isPluggedIn: Battery.isPluggedIn
readonly property real percentage: Battery.percentage
readonly property bool isLow: percentage <= Config.options.battery.low / 100
implicitHeight: batteryProgress.implicitHeight
hoverEnabled: true
ClippedProgressBar {
id: batteryProgress
anchors.centerIn: parent
vertical: true
valueBarWidth: 21
valueBarHeight: 40
value: percentage
highlightColor: (isLow && !isCharging) ? Appearance.m3colors.m3error : Appearance.colors.colOnSecondaryContainer
font {
pixelSize: text.length > 2 ? 11 : 13
weight: text.length > 2 ? Font.Medium : Font.DemiBold
}
textMask: Item {
anchors.centerIn: parent
width: batteryProgress.valueBarWidth
height: batteryProgress.valueBarHeight
ColumnLayout {
anchors.centerIn: parent
spacing: 0
MaterialSymbol {
id: boltIcon
Layout.alignment: Qt.AlignHCenter
fill: 1
text: isCharging ? "bolt" : "battery_android_full"
iconSize: Appearance.font.pixelSize.normal
animateChange: true
}
StyledText {
Layout.alignment: Qt.AlignHCenter
font: batteryProgress.font
text: batteryProgress.text
}
}
}
}
Bar.BatteryPopup {
id: batteryPopup
hoverTarget: root
}
}
@@ -0,0 +1,40 @@
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
import QtQuick
Item {
id: root
required property string iconName
required property double percentage
property int warningThreshold: 100
implicitHeight: resourceProgress.implicitHeight
implicitWidth: Appearance.sizes.verticalBarWidth
property bool warning: percentage * 100 >= warningThreshold
ClippedFilledCircularProgress {
id: resourceProgress
anchors.centerIn: parent
value: percentage
enableAnimation: false
colPrimary: root.warning ? Appearance.colors.colError : Appearance.colors.colOnSecondaryContainer
accountForLightBleeding: !root.warning
MaterialSymbol {
font.weight: Font.Medium
fill: 1
text: root.iconName
iconSize: 13
color: Appearance.colors.colOnSecondaryContainer
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
enabled: root.visible
}
}
@@ -0,0 +1,45 @@
import qs.services
import qs.modules.common
import QtQuick
import QtQuick.Layouts
import "../bar" as Bar
MouseArea {
id: root
property bool alwaysShowAllResources: false
implicitHeight: columnLayout.implicitHeight
implicitWidth: columnLayout.implicitWidth
hoverEnabled: true
ColumnLayout {
id: columnLayout
spacing: 10
anchors.fill: parent
Resource {
Layout.alignment: Qt.AlignHCenter
iconName: "memory"
percentage: ResourceUsage.memoryUsedPercentage
warningThreshold: Config.options.bar.resources.memoryWarningThreshold
}
Resource {
Layout.alignment: Qt.AlignHCenter
iconName: "swap_horiz"
percentage: ResourceUsage.swapUsedPercentage
warningThreshold: Config.options.bar.resources.swapWarningThreshold
}
Resource {
Layout.alignment: Qt.AlignHCenter
iconName: "planner_review"
percentage: ResourceUsage.cpuUsage
warningThreshold: Config.options.bar.resources.cpuWarningThreshold
}
}
Bar.ResourcesPopup {
hoverTarget: root
}
}
@@ -0,0 +1,245 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Services.UPower
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
Scope {
id: bar
property bool showBarBackground: Config.options.bar.showBackground
Variants {
// For each monitor
model: {
const screens = Quickshell.screens;
const list = Config.options.bar.screenList;
if (!list || list.length === 0)
return screens;
return screens.filter(screen => list.includes(screen.name));
}
LazyLoader {
id: barLoader
active: GlobalStates.barOpen && !GlobalStates.screenLocked
required property ShellScreen modelData
component: PanelWindow { // Bar window
id: barRoot
screen: barLoader.modelData
property var brightnessMonitor: Brightness.getMonitorForScreen(barLoader.modelData)
Timer {
id: showBarTimer
interval: (Config?.options.bar.autoHide.showWhenPressingSuper.delay ?? 100)
repeat: false
onTriggered: {
barRoot.superShow = true
}
}
Connections {
target: GlobalStates
function onSuperDownChanged() {
if (!Config?.options.bar.autoHide.showWhenPressingSuper.enable) return;
if (GlobalStates.superDown) showBarTimer.restart();
else {
showBarTimer.stop();
barRoot.superShow = false;
}
}
}
property bool superShow: false
property bool mustShow: hoverRegion.containsMouse || superShow
exclusionMode: ExclusionMode.Ignore
exclusiveZone: (Config?.options.bar.autoHide.enable && (!mustShow || !Config?.options.bar.autoHide.pushWindows)) ? 0 :
Appearance.sizes.baseVerticalBarWidth + (Config.options.bar.cornerStyle === 1 ? Appearance.sizes.hyprlandGapsOut : 0)
WlrLayershell.namespace: "quickshell:verticalBar"
// WlrLayershell.layer: WlrLayer.Overlay // TODO enable this when bar can hide when fullscreen
implicitWidth: Appearance.sizes.verticalBarWidth + Appearance.rounding.screenRounding
mask: Region {
item: hoverMaskRegion
}
color: "transparent"
anchors {
left: !Config.options.bar.bottom
right: Config.options.bar.bottom
top: true
bottom: true
}
MouseArea {
id: hoverRegion
hoverEnabled: true
anchors.fill: parent
Item {
id: hoverMaskRegion
anchors {
fill: barContent
leftMargin: -Config.options.bar.autoHide.hoverRegionWidth
rightMargin: -Config.options.bar.autoHide.hoverRegionWidth
}
}
VerticalBarContent {
id: barContent
implicitWidth: Appearance.sizes.verticalBarWidth
anchors {
top: parent.top
bottom: parent.bottom
left: parent.left
right: undefined
leftMargin: (Config?.options.bar.autoHide.enable && !mustShow) ? -Appearance.sizes.verticalBarWidth : 0
rightMargin: 0
}
Behavior on anchors.leftMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
Behavior on anchors.rightMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
states: State {
name: "right"
when: Config.options.bar.bottom
AnchorChanges {
target: barContent
anchors {
top: parent.top
bottom: parent.bottom
left: undefined
right: parent.right
}
}
PropertyChanges {
target: barContent
anchors.topMargin: 0
anchors.rightMargin: (Config?.options.bar.autoHide.enable && !mustShow) ? -Appearance.sizes.barHeight : 0
}
}
}
// Round decorators
Loader {
id: roundDecorators
anchors {
top: parent.top
bottom: parent.bottom
left: barContent.right
right: undefined
}
width: Appearance.rounding.screenRounding
active: showBarBackground && Config.options.bar.cornerStyle === 0 // Hug
states: State {
name: "right"
when: Config.options.bar.bottom
AnchorChanges {
target: roundDecorators
anchors {
top: parent.top
bottom: parent.bottom
left: undefined
right: barContent.left
}
}
}
sourceComponent: Item {
implicitHeight: Appearance.rounding.screenRounding
RoundCorner {
id: topCorner
anchors {
left: parent.left
right: parent.right
top: parent.top
}
implicitSize: Appearance.rounding.screenRounding
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
corner: RoundCorner.CornerEnum.TopLeft
states: State {
name: "bottom"
when: Config.options.bar.bottom
PropertyChanges {
topCorner.corner: RoundCorner.CornerEnum.TopRight
}
}
}
RoundCorner {
id: bottomCorner
anchors {
bottom: parent.bottom
left: !Config.options.bar.bottom ? parent.left : undefined
right: Config.options.bar.bottom ? parent.right : undefined
}
implicitSize: Appearance.rounding.screenRounding
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
corner: RoundCorner.CornerEnum.BottomLeft
states: State {
name: "bottom"
when: Config.options.bar.bottom
PropertyChanges {
bottomCorner.corner: RoundCorner.CornerEnum.BottomRight
}
}
}
}
}
}
}
}
}
IpcHandler {
target: "bar"
function toggle(): void {
GlobalStates.barOpen = !GlobalStates.barOpen
}
function close(): void {
GlobalStates.barOpen = false
}
function open(): void {
GlobalStates.barOpen = true
}
}
GlobalShortcut {
name: "barToggle"
description: "Toggles bar on press"
onPressed: {
GlobalStates.barOpen = !GlobalStates.barOpen;
}
}
GlobalShortcut {
name: "barOpen"
description: "Opens bar on press"
onPressed: {
GlobalStates.barOpen = true;
}
}
GlobalShortcut {
name: "barClose"
description: "Closes bar on press"
onPressed: {
GlobalStates.barOpen = false;
}
}
}
@@ -0,0 +1,306 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Bluetooth
import Quickshell.Services.UPower
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import "../bar" as Bar
Item { // Bar content region
id: root
property var screen: root.QsWindow.window?.screen
property var brightnessMonitor: Brightness.getMonitorForScreen(screen)
component HorizontalBarSeparator: Rectangle {
Layout.leftMargin: Appearance.sizes.baseBarHeight / 3
Layout.rightMargin: Appearance.sizes.baseBarHeight / 3
Layout.fillWidth: true
implicitHeight: 1
color: Appearance.colors.colOutlineVariant
}
// Background shadow
Loader {
active: Config.options.bar.showBackground && Config.options.bar.cornerStyle === 1
anchors.fill: barBackground
sourceComponent: StyledRectangularShadow {
anchors.fill: undefined // The loader's anchors act on this, and this should not have any anchor
target: barBackground
}
}
// Background
Rectangle {
id: barBackground
anchors {
fill: parent
margins: Config.options.bar.cornerStyle === 1 ? (Appearance.sizes.hyprlandGapsOut) : 0 // idk why but +1 is needed
}
color: Config.options.bar.showBackground ? Appearance.colors.colLayer0 : "transparent"
radius: Config.options.bar.cornerStyle === 1 ? Appearance.rounding.windowRounding : 0
border.width: Config.options.bar.cornerStyle === 1 ? 1 : 0
border.color: Appearance.colors.colLayer0Border
}
FocusedScrollMouseArea { // Top section | scroll to change brightness
id: barTopSectionMouseArea
anchors.top: parent.top
implicitHeight: topSectionColumnLayout.implicitHeight
implicitWidth: Appearance.sizes.baseVerticalBarWidth
height: (root.height - middleSection.height) / 2
width: Appearance.sizes.verticalBarWidth
onScrollDown: root.brightnessMonitor.setBrightness(root.brightnessMonitor.brightness - 0.05)
onScrollUp: root.brightnessMonitor.setBrightness(root.brightnessMonitor.brightness + 0.05)
onMovedAway: GlobalStates.osdBrightnessOpen = false
onPressed: event => {
if (event.button === Qt.LeftButton)
GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
}
ColumnLayout { // Content
id: topSectionColumnLayout
anchors.fill: parent
spacing: 10
Bar.LeftSidebarButton { // Left sidebar button
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: (Appearance.sizes.baseVerticalBarWidth - implicitWidth) / 2 + Appearance.sizes.hyprlandGapsOut
colBackground: barTopSectionMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
}
Item {
Layout.fillHeight: true
}
}
}
Column { // Middle section
id: middleSection
anchors.centerIn: parent
spacing: 4
Bar.BarGroup {
vertical: true
padding: 8
Resources {
Layout.fillWidth: true
Layout.fillHeight: false
}
HorizontalBarSeparator {}
VerticalMedia {
Layout.fillWidth: true
Layout.fillHeight: false
}
}
HorizontalBarSeparator {
visible: Config.options?.bar.borderless
}
Bar.BarGroup {
id: middleCenterGroup
vertical: true
padding: 6
Bar.Workspaces {
id: workspacesWidget
vertical: true
MouseArea {
// Right-click to toggle overview
anchors.fill: parent
acceptedButtons: Qt.RightButton
onPressed: event => {
if (event.button === Qt.RightButton) {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
}
}
}
}
}
HorizontalBarSeparator {
visible: Config.options?.bar.borderless
}
Bar.BarGroup {
vertical: true
padding: 8
VerticalClockWidget {
Layout.fillWidth: true
Layout.fillHeight: false
}
HorizontalBarSeparator {}
VerticalDateWidget {
Layout.fillWidth: true
Layout.fillHeight: false
}
HorizontalBarSeparator {
visible: UPower.displayDevice.isLaptopBattery
}
BatteryIndicator {
visible: UPower.displayDevice.isLaptopBattery
Layout.fillWidth: true
Layout.fillHeight: false
}
}
}
FocusedScrollMouseArea { // Bottom section | scroll to change volume
id: barBottomSectionMouseArea
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
implicitWidth: Appearance.sizes.baseVerticalBarWidth
implicitHeight: bottomSectionColumnLayout.implicitHeight
onScrollDown: {
const currentVolume = Audio.value;
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
Audio.sink.audio.volume -= step;
}
onScrollUp: {
const currentVolume = Audio.value;
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
Audio.sink.audio.volume = Math.min(1, Audio.sink.audio.volume + step);
}
onMovedAway: GlobalStates.osdVolumeOpen = false;
onPressed: event => {
if (event.button === Qt.LeftButton) {
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
}
}
ColumnLayout {
id: bottomSectionColumnLayout
anchors.fill: parent
spacing: 4
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
Bar.SysTray {
vertical: true
Layout.fillWidth: true
Layout.fillHeight: false
invertSide: Config?.options.bar.bottom
}
RippleButton { // Right sidebar button
id: rightSidebarButton
Layout.alignment: Qt.AlignBottom | Qt.AlignHCenter
Layout.bottomMargin: Appearance.rounding.screenRounding
Layout.fillHeight: false
implicitHeight: indicatorsColumnLayout.implicitHeight + 4 * 2
implicitWidth: indicatorsColumnLayout.implicitWidth + 6 * 2
buttonRadius: Appearance.rounding.full
colBackground: barBottomSectionMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
colBackgroundHover: Appearance.colors.colLayer1Hover
colRipple: Appearance.colors.colLayer1Active
colBackgroundToggled: Appearance.colors.colSecondaryContainer
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
colRippleToggled: Appearance.colors.colSecondaryContainerActive
toggled: GlobalStates.sidebarRightOpen
property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0
Behavior on colText {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
onPressed: {
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
}
ColumnLayout {
id: indicatorsColumnLayout
anchors.centerIn: parent
property real realSpacing: 6
spacing: 0
Revealer {
vertical: true
reveal: Audio.sink?.audio?.muted ?? false
Layout.fillWidth: true
Layout.bottomMargin: reveal ? indicatorsColumnLayout.realSpacing : 0
Behavior on Layout.bottomMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
MaterialSymbol {
text: "volume_off"
iconSize: Appearance.font.pixelSize.larger
color: rightSidebarButton.colText
}
}
Revealer {
vertical: true
reveal: Audio.source?.audio?.muted ?? false
Layout.fillWidth: true
Layout.bottomMargin: reveal ? indicatorsColumnLayout.realSpacing : 0
Behavior on Layout.topMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
MaterialSymbol {
text: "mic_off"
iconSize: Appearance.font.pixelSize.larger
color: rightSidebarButton.colText
}
}
Bar.HyprlandXkbIndicator {
vertical: true
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: indicatorsColumnLayout.realSpacing
color: rightSidebarButton.colText
}
Revealer {
vertical: true
reveal: Notifications.silent || Notifications.unread > 0
Layout.fillWidth: true
Layout.bottomMargin: reveal ? indicatorsColumnLayout.realSpacing : 0
implicitHeight: reveal ? notificationUnreadCount.implicitHeight : 0
implicitWidth: reveal ? notificationUnreadCount.implicitWidth : 0
Behavior on Layout.bottomMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
Bar.NotificationUnreadCount {
id: notificationUnreadCount
}
}
MaterialSymbol {
Layout.bottomMargin: indicatorsColumnLayout.realSpacing
text: Network.materialSymbol
iconSize: Appearance.font.pixelSize.larger
color: rightSidebarButton.colText
}
MaterialSymbol {
visible: BluetoothStatus.available
text: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled"
iconSize: Appearance.font.pixelSize.larger
color: rightSidebarButton.colText
}
}
}
}
}
}
@@ -0,0 +1,43 @@
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import QtQuick.Layouts
import "../bar" as Bar
Item {
id: root
property bool borderless: Config.options.bar.borderless
implicitHeight: clockColumn.implicitHeight
implicitWidth: Appearance.sizes.verticalBarWidth
ColumnLayout {
id: clockColumn
anchors.centerIn: parent
spacing: 0
Repeater {
model: DateTime.time.split(/[: ]/)
delegate: StyledText {
required property string modelData
Layout.alignment: Qt.AlignHCenter
font.pixelSize: modelData.match(/am|pm/i) ?
Appearance.font.pixelSize.smaller // Smaller "am"/"pm" text
: Appearance.font.pixelSize.large
color: Appearance.colors.colOnLayer1
text: modelData.padStart(2, "0")
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
Bar.ClockWidgetTooltip {
hoverTarget: mouseArea
}
}
}
@@ -0,0 +1,64 @@
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import QtQuick.Shapes
import QtQuick.Layouts
import "../bar" as Bar
Item { // Full hitbox
id: root
implicitHeight: content.implicitHeight
implicitWidth: Appearance.sizes.verticalBarWidth
property var dayOfMonth: DateTime.shortDate.split(/[-\/]/)[0] // What if 🍔murica🦅? good question
property var monthOfYear: DateTime.shortDate.split(/[-\/]/)[1]
Item { // Boundaries for date numbers
id: content
anchors.centerIn: parent
implicitWidth: 24
implicitHeight: 30
Shape {
id: diagonalLine
property real padding: 4
anchors.fill: parent
preferredRendererType: Shape.CurveRenderer
ShapePath {
strokeWidth: 1.2
strokeColor: Appearance.colors.colSubtext
fillColor: "transparent"
startX: content.width - diagonalLine.padding
startY: diagonalLine.padding
PathLine {
x: diagonalLine.padding
y: content.height - diagonalLine.padding
}
}
}
StyledText {
id: dayText
anchors {
top: parent.top
left: parent.left
}
font.pixelSize: 13
color: Appearance.colors.colOnLayer1
text: dayOfMonth
}
StyledText {
id: monthText
anchors {
bottom: parent.bottom
right: parent.right
}
font.pixelSize: 13
color: Appearance.colors.colOnLayer1
text: monthOfYear
}
}
}
@@ -0,0 +1,106 @@
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import qs
import qs.modules.common.functions
import QtQuick
import QtQuick.Layouts
import Quickshell.Services.Mpris
import "../bar" as Bar
MouseArea {
id: root
property bool borderless: Config.options.bar.borderless
readonly property MprisPlayer activePlayer: MprisController.activePlayer
readonly property string cleanedTitle: StringUtils.cleanMusicTitle(activePlayer?.trackTitle) || Translation.tr("No media")
Layout.fillHeight: true
implicitHeight: mediaCircProg.implicitHeight
implicitWidth: Appearance.sizes.verticalBarWidth
Timer {
running: activePlayer?.playbackState == MprisPlaybackState.Playing
interval: Config.options.resources.updateInterval
repeat: true
onTriggered: activePlayer.positionChanged()
}
acceptedButtons: Qt.MiddleButton | Qt.BackButton | Qt.ForwardButton | Qt.RightButton | Qt.LeftButton
hoverEnabled: true
onPressed: (event) => {
if (event.button === Qt.MiddleButton) {
activePlayer.togglePlaying();
} else if (event.button === Qt.BackButton) {
activePlayer.previous();
} else if (event.button === Qt.ForwardButton || event.button === Qt.RightButton) {
activePlayer.next();
} else if (event.button === Qt.LeftButton) {
GlobalStates.mediaControlsOpen = !GlobalStates.mediaControlsOpen
}
}
ClippedFilledCircularProgress {
id: mediaCircProg
anchors.centerIn: parent
implicitSize: 20
lineWidth: Appearance.rounding.unsharpen
value: activePlayer?.position / activePlayer?.length
colPrimary: Appearance.colors.colOnSecondaryContainer
enableAnimation: false
Item {
anchors.centerIn: parent
width: mediaCircProg.implicitSize
height: mediaCircProg.implicitSize
MaterialSymbol {
anchors.centerIn: parent
fill: 1
text: activePlayer?.isPlaying ? "pause" : "music_note"
iconSize: Appearance.font.pixelSize.normal
color: Appearance.m3colors.m3onSecondaryContainer
}
}
}
Bar.StyledPopup {
hoverTarget: root
active: GlobalStates.mediaControlsOpen ? false : root.containsMouse
Column {
anchors.centerIn: parent
spacing: 4
Row {
spacing: 4
MaterialSymbol {
anchors.verticalCenter: parent.verticalCenter
fill: 0
font.weight: Font.Medium
text: "music_note"
iconSize: Appearance.font.pixelSize.large
color: Appearance.colors.colOnSurfaceVariant
}
StyledText {
anchors.verticalCenter: parent.verticalCenter
text: "Media"
font {
weight: Font.Medium
pixelSize: Appearance.font.pixelSize.normal
}
color: Appearance.colors.colOnSurfaceVariant
}
}
StyledText {
text: `${cleanedTitle}${activePlayer?.trackArtist ? '\n' + activePlayer.trackArtist : ''}`
}
}
}
}